@cemalidev/trate 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +16 -0
- package/.github/workflows/ci.yml +61 -0
- package/.github/workflows/cleanup.yml +36 -0
- package/.github/workflows/release.yml +56 -0
- package/.husky/pre-commit +4 -0
- package/.prettierrc +10 -0
- package/CHANGELOG.md +40 -0
- package/CONTRIBUTING.md +120 -0
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/build.cjs +27 -0
- package/dist/index.js +1587 -0
- package/eslint.config.mjs +42 -0
- package/package.json +67 -0
- package/src/config/config.ts +130 -0
- package/src/config/types.ts +9 -0
- package/src/convert.ts +562 -0
- package/src/dashboard.ts +76 -0
- package/src/i18n/de.ts +90 -0
- package/src/i18n/en.ts +94 -0
- package/src/i18n/es.ts +90 -0
- package/src/i18n/fr.ts +90 -0
- package/src/i18n/index.ts +108 -0
- package/src/i18n/tr.ts +90 -0
- package/src/index.ts +390 -0
- package/src/logo.ts +33 -0
- package/src/types/errors.ts +38 -0
- package/src/ui.ts +40 -0
- package/src/utils/logger.ts +84 -0
- package/tests/config.test.ts +95 -0
- package/tests/i18n.test.ts +121 -0
- package/tests/validation.test.ts +149 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +15 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1587 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { program } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/config/config.ts
|
|
7
|
+
import Conf from "conf";
|
|
8
|
+
|
|
9
|
+
// src/i18n/tr.ts
|
|
10
|
+
var tr = {
|
|
11
|
+
app: {
|
|
12
|
+
name: "trate",
|
|
13
|
+
tagline: "Terminal d\xF6viz kuru takip\xE7isi"
|
|
14
|
+
},
|
|
15
|
+
error: {
|
|
16
|
+
noConnection: "\u0130nternet ba\u011Flant\u0131n\u0131z\u0131 kontrol ediniz",
|
|
17
|
+
notFound: "bulunamad\u0131",
|
|
18
|
+
invalidCurrency: "Ge\xE7ersiz para birimi",
|
|
19
|
+
goldPricesUnavailable: "Alt\u0131n fiyatlar\u0131 \u015Fu anda mevcut de\u011Fil",
|
|
20
|
+
goldPricesLoadFailed: "Alt\u0131n fiyatlar\u0131 y\xFCklenemedi",
|
|
21
|
+
sameCurrency: "Ayn\u0131 para birimi d\xF6n\xFC\u015F\xFCm\xFC",
|
|
22
|
+
rateNotFound: "kuru bulunamad\u0131"
|
|
23
|
+
},
|
|
24
|
+
success: {
|
|
25
|
+
added: "Eklendi",
|
|
26
|
+
removed: "\xC7\u0131kar\u0131ld\u0131",
|
|
27
|
+
cacheCleared: "Cache temizlendi",
|
|
28
|
+
settingsReset: "Ayarlar s\u0131f\u0131rland\u0131",
|
|
29
|
+
baseCurrencySet: "Base para birimi"
|
|
30
|
+
},
|
|
31
|
+
ui: {
|
|
32
|
+
base: "Base",
|
|
33
|
+
rate: "Kur",
|
|
34
|
+
date: "Tarih",
|
|
35
|
+
type: "Tip",
|
|
36
|
+
symbol: "Sembol",
|
|
37
|
+
price: "Fiyat",
|
|
38
|
+
info: "Info",
|
|
39
|
+
tip: "\u0130pucu",
|
|
40
|
+
noConversionNeeded: "(d\xF6n\xFC\u015F\xFCme gerek yok)",
|
|
41
|
+
loading: "loading..."
|
|
42
|
+
},
|
|
43
|
+
commands: {
|
|
44
|
+
setBase: "set-base",
|
|
45
|
+
list: "list",
|
|
46
|
+
favs: "favs",
|
|
47
|
+
add: "add",
|
|
48
|
+
remove: "remove",
|
|
49
|
+
help: "help",
|
|
50
|
+
refresh: "refresh",
|
|
51
|
+
reset: "reset",
|
|
52
|
+
moon: "moon",
|
|
53
|
+
setLang: "set-lang"
|
|
54
|
+
},
|
|
55
|
+
dashboard: {
|
|
56
|
+
title: "Favoriler Panosu",
|
|
57
|
+
favorites: "Favoriler",
|
|
58
|
+
usage: "H\u0131zl\u0131 kur i\xE7in",
|
|
59
|
+
usageAdd: "Favori eklemek i\xE7in",
|
|
60
|
+
type: {
|
|
61
|
+
fiat: "FIAT",
|
|
62
|
+
crypto: "KR\u0130PTO",
|
|
63
|
+
metal: "METAL",
|
|
64
|
+
gold: "ALTIN"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
help: {
|
|
68
|
+
usage: "Kullan\u0131m",
|
|
69
|
+
examples: "\xD6rnekler",
|
|
70
|
+
crypto: "Kripto",
|
|
71
|
+
metals: "Metaller",
|
|
72
|
+
jewelry: "Kuyumculuk",
|
|
73
|
+
config: "Ayarlar",
|
|
74
|
+
alias: "\xD6zel K\u0131sayollar",
|
|
75
|
+
aliasCreate: "K\u0131sayol olu\u015Ftur",
|
|
76
|
+
aliasView: "K\u0131sayollar\u0131 g\xF6r"
|
|
77
|
+
},
|
|
78
|
+
moon: {
|
|
79
|
+
toTheMoon: "Ay'a!",
|
|
80
|
+
goingToMoon: "AY'A G\u0130D\u0130YORUZ!",
|
|
81
|
+
tip: "G\xFCncel fiyatlar\u0131 g\xF6rmek i\xE7in"
|
|
82
|
+
},
|
|
83
|
+
display: {
|
|
84
|
+
goldOunce: "Alt\u0131n Ons",
|
|
85
|
+
goldKg: "Gram Alt\u0131n",
|
|
86
|
+
silverKg: "G\xFCm\xFC\u015F Kg",
|
|
87
|
+
silverOunce: "G\xFCm\xFC\u015F Ons",
|
|
88
|
+
quarterGold: "\xC7eyrek Alt\u0131n",
|
|
89
|
+
halfGold: "Yar\u0131m Alt\u0131n",
|
|
90
|
+
fullGold: "Tam Alt\u0131n",
|
|
91
|
+
ataGold: "Ata Alt\u0131n",
|
|
92
|
+
gremseGold: "Gremse Alt\u0131n"
|
|
93
|
+
},
|
|
94
|
+
cli: {
|
|
95
|
+
specifyCurrency: "Bir para birimi belirtin",
|
|
96
|
+
usage: "Kullan\u0131m",
|
|
97
|
+
invalidCommand: "Ge\xE7ersiz komut"
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/i18n/en.ts
|
|
102
|
+
var en = {
|
|
103
|
+
app: {
|
|
104
|
+
name: "trate",
|
|
105
|
+
tagline: "Terminal exchange rate tracker"
|
|
106
|
+
},
|
|
107
|
+
error: {
|
|
108
|
+
noConnection: "Check your internet connection",
|
|
109
|
+
notFound: "not found",
|
|
110
|
+
invalidCurrency: "Invalid currency",
|
|
111
|
+
goldPricesUnavailable: "Gold prices are currently unavailable",
|
|
112
|
+
goldPricesLoadFailed: "Failed to load gold prices",
|
|
113
|
+
sameCurrency: "Same currency conversion",
|
|
114
|
+
rateNotFound: "rate not found"
|
|
115
|
+
},
|
|
116
|
+
success: {
|
|
117
|
+
added: "Added",
|
|
118
|
+
removed: "Removed",
|
|
119
|
+
cacheCleared: "Cache cleared",
|
|
120
|
+
settingsReset: "Settings reset",
|
|
121
|
+
baseCurrencySet: "Base currency"
|
|
122
|
+
},
|
|
123
|
+
ui: {
|
|
124
|
+
base: "Base",
|
|
125
|
+
rate: "Rate",
|
|
126
|
+
date: "Date",
|
|
127
|
+
type: "Type",
|
|
128
|
+
symbol: "Symbol",
|
|
129
|
+
price: "Price",
|
|
130
|
+
info: "Info",
|
|
131
|
+
tip: "Tip",
|
|
132
|
+
noConversionNeeded: "(no conversion needed)",
|
|
133
|
+
loading: "loading..."
|
|
134
|
+
},
|
|
135
|
+
commands: {
|
|
136
|
+
setBase: "set-base",
|
|
137
|
+
list: "list",
|
|
138
|
+
favs: "favs",
|
|
139
|
+
add: "add",
|
|
140
|
+
remove: "remove",
|
|
141
|
+
help: "help",
|
|
142
|
+
refresh: "refresh",
|
|
143
|
+
reset: "reset",
|
|
144
|
+
moon: "moon",
|
|
145
|
+
setLang: "set-lang",
|
|
146
|
+
alias: "alias",
|
|
147
|
+
aliasAdd: "alias add",
|
|
148
|
+
aliasRemove: "alias remove",
|
|
149
|
+
aliasList: "alias list"
|
|
150
|
+
},
|
|
151
|
+
dashboard: {
|
|
152
|
+
title: "Favorites Dashboard",
|
|
153
|
+
favorites: "Favorites",
|
|
154
|
+
usage: "Use for quick rate",
|
|
155
|
+
usageAdd: "Use to add favorites",
|
|
156
|
+
type: {
|
|
157
|
+
fiat: "FIAT",
|
|
158
|
+
crypto: "CRYPTO",
|
|
159
|
+
metal: "METAL",
|
|
160
|
+
gold: "GOLD"
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
help: {
|
|
164
|
+
usage: "Usage",
|
|
165
|
+
examples: "Examples",
|
|
166
|
+
crypto: "Crypto",
|
|
167
|
+
metals: "Metals",
|
|
168
|
+
jewelry: "Jewelry",
|
|
169
|
+
config: "Config",
|
|
170
|
+
alias: "Custom Aliases",
|
|
171
|
+
aliasCreate: "Create shortcut",
|
|
172
|
+
aliasView: "View aliases"
|
|
173
|
+
},
|
|
174
|
+
moon: {
|
|
175
|
+
toTheMoon: "To The Moon!",
|
|
176
|
+
goingToMoon: "WE ARE GOING TO THE MOON!",
|
|
177
|
+
tip: "Use for current prices"
|
|
178
|
+
},
|
|
179
|
+
display: {
|
|
180
|
+
goldOunce: "Gold Ounce",
|
|
181
|
+
goldKg: "Gram Gold",
|
|
182
|
+
silverKg: "Silver Kg",
|
|
183
|
+
silverOunce: "Silver Ounce",
|
|
184
|
+
quarterGold: "Quarter Gold",
|
|
185
|
+
halfGold: "Half Gold",
|
|
186
|
+
fullGold: "Full Gold",
|
|
187
|
+
ataGold: "Ata Gold",
|
|
188
|
+
gremseGold: "Gremse Gold"
|
|
189
|
+
},
|
|
190
|
+
cli: {
|
|
191
|
+
specifyCurrency: "Specify a currency",
|
|
192
|
+
usage: "Usage",
|
|
193
|
+
invalidCommand: "Invalid command"
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/i18n/de.ts
|
|
198
|
+
var de = {
|
|
199
|
+
app: {
|
|
200
|
+
name: "trate",
|
|
201
|
+
tagline: "Terminal-W\xE4hrungsrechner"
|
|
202
|
+
},
|
|
203
|
+
error: {
|
|
204
|
+
noConnection: "\xDCberpr\xFCfen Sie Ihre Internetverbindung",
|
|
205
|
+
notFound: "nicht gefunden",
|
|
206
|
+
invalidCurrency: "Ung\xFCltige W\xE4hrung",
|
|
207
|
+
goldPricesUnavailable: "Goldpreise sind derzeit nicht verf\xFCgbar",
|
|
208
|
+
goldPricesLoadFailed: "Goldpreise konnten nicht geladen werden",
|
|
209
|
+
sameCurrency: "Gleiche W\xE4hrungsumrechnung",
|
|
210
|
+
rateNotFound: "Kurs nicht gefunden"
|
|
211
|
+
},
|
|
212
|
+
success: {
|
|
213
|
+
added: "Hinzugef\xFCgt",
|
|
214
|
+
removed: "Entfernt",
|
|
215
|
+
cacheCleared: "Cache geleert",
|
|
216
|
+
settingsReset: "Einstellungen zur\xFCckgesetzt",
|
|
217
|
+
baseCurrencySet: "Basisw\xE4hrung"
|
|
218
|
+
},
|
|
219
|
+
ui: {
|
|
220
|
+
base: "Basis",
|
|
221
|
+
rate: "Kurs",
|
|
222
|
+
date: "Datum",
|
|
223
|
+
type: "Typ",
|
|
224
|
+
symbol: "Symbol",
|
|
225
|
+
price: "Preis",
|
|
226
|
+
info: "Info",
|
|
227
|
+
tip: "Tipp",
|
|
228
|
+
noConversionNeeded: "(keine Konvertierung n\xF6tig)",
|
|
229
|
+
loading: "lade..."
|
|
230
|
+
},
|
|
231
|
+
commands: {
|
|
232
|
+
setBase: "set-base",
|
|
233
|
+
list: "list",
|
|
234
|
+
favs: "favs",
|
|
235
|
+
add: "add",
|
|
236
|
+
remove: "remove",
|
|
237
|
+
help: "help",
|
|
238
|
+
refresh: "refresh",
|
|
239
|
+
reset: "reset",
|
|
240
|
+
moon: "moon",
|
|
241
|
+
setLang: "set-lang"
|
|
242
|
+
},
|
|
243
|
+
dashboard: {
|
|
244
|
+
title: "Favoriten-Dashboard",
|
|
245
|
+
favorites: "Favoriten",
|
|
246
|
+
usage: "F\xFCr Schnellkurs verwenden",
|
|
247
|
+
usageAdd: "Zum Hinzuf\xFCgen von Favoriten",
|
|
248
|
+
type: {
|
|
249
|
+
fiat: "FIAT",
|
|
250
|
+
crypto: "Krypto",
|
|
251
|
+
metal: "METALL",
|
|
252
|
+
gold: "GOLD"
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
help: {
|
|
256
|
+
usage: "Verwendung",
|
|
257
|
+
examples: "Beispiele",
|
|
258
|
+
crypto: "Krypto",
|
|
259
|
+
metals: "Metalle",
|
|
260
|
+
jewelry: "Schmuck",
|
|
261
|
+
config: "Konfiguration",
|
|
262
|
+
alias: "Benutzerdefinierte Aliase",
|
|
263
|
+
aliasCreate: "Shortcut erstellen",
|
|
264
|
+
aliasView: "Aliase anzeigen"
|
|
265
|
+
},
|
|
266
|
+
moon: {
|
|
267
|
+
toTheMoon: "Zum Mond!",
|
|
268
|
+
goingToMoon: "WIR FLIEGEN ZUM MOND!",
|
|
269
|
+
tip: "F\xFCr aktuelle Preise verwenden"
|
|
270
|
+
},
|
|
271
|
+
display: {
|
|
272
|
+
goldOunce: "Gold Unze",
|
|
273
|
+
goldKg: "Gramm Gold",
|
|
274
|
+
silverKg: "Silber Kg",
|
|
275
|
+
silverOunce: "Silber Unze",
|
|
276
|
+
quarterGold: "Viertel Gold",
|
|
277
|
+
halfGold: "Halbes Gold",
|
|
278
|
+
fullGold: "Volles Gold",
|
|
279
|
+
ataGold: "Ata Gold",
|
|
280
|
+
gremseGold: "Gremse Gold"
|
|
281
|
+
},
|
|
282
|
+
cli: {
|
|
283
|
+
specifyCurrency: "Geben Sie eine W\xE4hrung an",
|
|
284
|
+
usage: "Verwendung",
|
|
285
|
+
invalidCommand: "Ung\xFCltiger Befehl"
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// src/i18n/fr.ts
|
|
290
|
+
var fr = {
|
|
291
|
+
app: {
|
|
292
|
+
name: "trate",
|
|
293
|
+
tagline: "Suivi des taux de change pour terminal"
|
|
294
|
+
},
|
|
295
|
+
error: {
|
|
296
|
+
noConnection: "V\xE9rifiez votre connexion internet",
|
|
297
|
+
notFound: "non trouv\xE9",
|
|
298
|
+
invalidCurrency: "Devise invalide",
|
|
299
|
+
goldPricesUnavailable: "Les prix de l'or ne sont actuellement pas disponibles",
|
|
300
|
+
goldPricesLoadFailed: "\xC9chec du chargement des prix de l'or",
|
|
301
|
+
sameCurrency: "Conversion de m\xEAme devise",
|
|
302
|
+
rateNotFound: "taux non trouv\xE9"
|
|
303
|
+
},
|
|
304
|
+
success: {
|
|
305
|
+
added: "Ajout\xE9",
|
|
306
|
+
removed: "Supprim\xE9",
|
|
307
|
+
cacheCleared: "Cache vid\xE9",
|
|
308
|
+
settingsReset: "Param\xE8tres r\xE9initialis\xE9s",
|
|
309
|
+
baseCurrencySet: "Devise de base"
|
|
310
|
+
},
|
|
311
|
+
ui: {
|
|
312
|
+
base: "Base",
|
|
313
|
+
rate: "Taux",
|
|
314
|
+
date: "Date",
|
|
315
|
+
type: "Type",
|
|
316
|
+
symbol: "Symbole",
|
|
317
|
+
price: "Prix",
|
|
318
|
+
info: "Info",
|
|
319
|
+
tip: "Astuce",
|
|
320
|
+
noConversionNeeded: "(pas de conversion n\xE9cessaire)",
|
|
321
|
+
loading: "chargement..."
|
|
322
|
+
},
|
|
323
|
+
commands: {
|
|
324
|
+
setBase: "set-base",
|
|
325
|
+
list: "list",
|
|
326
|
+
favs: "favs",
|
|
327
|
+
add: "add",
|
|
328
|
+
remove: "remove",
|
|
329
|
+
help: "help",
|
|
330
|
+
refresh: "refresh",
|
|
331
|
+
reset: "reset",
|
|
332
|
+
moon: "moon",
|
|
333
|
+
setLang: "set-lang"
|
|
334
|
+
},
|
|
335
|
+
dashboard: {
|
|
336
|
+
title: "Tableau de bord des favoris",
|
|
337
|
+
favorites: "Favoris",
|
|
338
|
+
usage: "Utiliser pour le taux rapide",
|
|
339
|
+
usageAdd: "Utiliser pour ajouter des favoris",
|
|
340
|
+
type: {
|
|
341
|
+
fiat: "FIAT",
|
|
342
|
+
crypto: "CRYPTO",
|
|
343
|
+
metal: "M\xC9TAUX",
|
|
344
|
+
gold: "OR"
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
help: {
|
|
348
|
+
usage: "Utilisation",
|
|
349
|
+
examples: "Exemples",
|
|
350
|
+
crypto: "Crypto",
|
|
351
|
+
metals: "M\xE9taux",
|
|
352
|
+
jewelry: "Bijouterie",
|
|
353
|
+
config: "Configuration",
|
|
354
|
+
alias: "Alias Personnalis\xE9s",
|
|
355
|
+
aliasCreate: "Cr\xE9er raccourci",
|
|
356
|
+
aliasView: "Voir alias"
|
|
357
|
+
},
|
|
358
|
+
moon: {
|
|
359
|
+
toTheMoon: "Vers la Lune!",
|
|
360
|
+
goingToMoon: "NOUS ALLONS VERS LA LUNE!",
|
|
361
|
+
tip: "Utiliser pour les prix actuels"
|
|
362
|
+
},
|
|
363
|
+
display: {
|
|
364
|
+
goldOunce: "Once d'or",
|
|
365
|
+
goldKg: "Or Gramme",
|
|
366
|
+
silverKg: "Argent Kg",
|
|
367
|
+
silverOunce: "Once d'argent",
|
|
368
|
+
quarterGold: "Quart d'or",
|
|
369
|
+
halfGold: "Demi-or",
|
|
370
|
+
fullGold: "Or entier",
|
|
371
|
+
ataGold: "Or Ata",
|
|
372
|
+
gremseGold: "Or Gremse"
|
|
373
|
+
},
|
|
374
|
+
cli: {
|
|
375
|
+
specifyCurrency: "Sp\xE9cifiez une devise",
|
|
376
|
+
usage: "Utilisation",
|
|
377
|
+
invalidCommand: "Commande invalide"
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// src/i18n/es.ts
|
|
382
|
+
var es = {
|
|
383
|
+
app: {
|
|
384
|
+
name: "trate",
|
|
385
|
+
tagline: "Rastreador de tipos de cambio para terminal"
|
|
386
|
+
},
|
|
387
|
+
error: {
|
|
388
|
+
noConnection: "Verifica tu conexi\xF3n a internet",
|
|
389
|
+
notFound: "no encontrado",
|
|
390
|
+
invalidCurrency: "Moneda inv\xE1lida",
|
|
391
|
+
goldPricesUnavailable: "Los precios del oro no est\xE1n disponibles actualmente",
|
|
392
|
+
goldPricesLoadFailed: "Error al cargar los precios del oro",
|
|
393
|
+
sameCurrency: "Conversi\xF3n de la misma moneda",
|
|
394
|
+
rateNotFound: "tipo de cambio no encontrado"
|
|
395
|
+
},
|
|
396
|
+
success: {
|
|
397
|
+
added: "A\xF1adido",
|
|
398
|
+
removed: "Eliminado",
|
|
399
|
+
cacheCleared: "Cach\xE9 limpiado",
|
|
400
|
+
settingsReset: "Configuraci\xF3n reiniciada",
|
|
401
|
+
baseCurrencySet: "Moneda base"
|
|
402
|
+
},
|
|
403
|
+
ui: {
|
|
404
|
+
base: "Base",
|
|
405
|
+
rate: "Tipo",
|
|
406
|
+
date: "Fecha",
|
|
407
|
+
type: "Tipo",
|
|
408
|
+
symbol: "S\xEDmbolo",
|
|
409
|
+
price: "Precio",
|
|
410
|
+
info: "Info",
|
|
411
|
+
tip: "Consejo",
|
|
412
|
+
noConversionNeeded: "(sin conversi\xF3n necesaria)",
|
|
413
|
+
loading: "cargando..."
|
|
414
|
+
},
|
|
415
|
+
commands: {
|
|
416
|
+
setBase: "set-base",
|
|
417
|
+
list: "list",
|
|
418
|
+
favs: "favs",
|
|
419
|
+
add: "add",
|
|
420
|
+
remove: "remove",
|
|
421
|
+
help: "help",
|
|
422
|
+
refresh: "refresh",
|
|
423
|
+
reset: "reset",
|
|
424
|
+
moon: "moon",
|
|
425
|
+
setLang: "set-lang"
|
|
426
|
+
},
|
|
427
|
+
dashboard: {
|
|
428
|
+
title: "Panel de favoritos",
|
|
429
|
+
favorites: "Favoritos",
|
|
430
|
+
usage: "Usar para tipo r\xE1pido",
|
|
431
|
+
usageAdd: "Usar para a\xF1adir favoritos",
|
|
432
|
+
type: {
|
|
433
|
+
fiat: "FIAT",
|
|
434
|
+
crypto: "CRIPTO",
|
|
435
|
+
metal: "METALES",
|
|
436
|
+
gold: "ORO"
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
help: {
|
|
440
|
+
usage: "Uso",
|
|
441
|
+
examples: "Ejemplos",
|
|
442
|
+
crypto: "Cripto",
|
|
443
|
+
metals: "Metales",
|
|
444
|
+
jewelry: "Joyer\xEDa",
|
|
445
|
+
config: "Config",
|
|
446
|
+
alias: "Alias Personalizados",
|
|
447
|
+
aliasCreate: "Crear atajo",
|
|
448
|
+
aliasView: "Ver aliases"
|
|
449
|
+
},
|
|
450
|
+
moon: {
|
|
451
|
+
toTheMoon: "\xA1A la Luna!",
|
|
452
|
+
goingToMoon: "\xA1VAMOS A LA LUNA!",
|
|
453
|
+
tip: "Usar para precios actuales"
|
|
454
|
+
},
|
|
455
|
+
display: {
|
|
456
|
+
goldOunce: "Onza de oro",
|
|
457
|
+
goldKg: "Oro Gramo",
|
|
458
|
+
silverKg: "Plata Kg",
|
|
459
|
+
silverOunce: "Onza de plata",
|
|
460
|
+
quarterGold: "Cuarto de oro",
|
|
461
|
+
halfGold: "Medio oro",
|
|
462
|
+
fullGold: "Oro entero",
|
|
463
|
+
ataGold: "Oro Ata",
|
|
464
|
+
gremseGold: "Oro Gremse"
|
|
465
|
+
},
|
|
466
|
+
cli: {
|
|
467
|
+
specifyCurrency: "Especifica una moneda",
|
|
468
|
+
usage: "Uso",
|
|
469
|
+
invalidCommand: "Comando inv\xE1lido"
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/i18n/index.ts
|
|
474
|
+
var translations = {
|
|
475
|
+
tr,
|
|
476
|
+
en,
|
|
477
|
+
de,
|
|
478
|
+
fr,
|
|
479
|
+
es
|
|
480
|
+
};
|
|
481
|
+
var supportedLocales = ["tr", "en", "de", "fr", "es"];
|
|
482
|
+
function getSystemLocale() {
|
|
483
|
+
const systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
484
|
+
const languageCode = systemLocale.split("-")[0].toLowerCase();
|
|
485
|
+
if (supportedLocales.includes(languageCode)) {
|
|
486
|
+
return languageCode;
|
|
487
|
+
}
|
|
488
|
+
return "en";
|
|
489
|
+
}
|
|
490
|
+
var currentLocale = null;
|
|
491
|
+
function getLocale() {
|
|
492
|
+
if (currentLocale) {
|
|
493
|
+
return currentLocale;
|
|
494
|
+
}
|
|
495
|
+
return getSystemLocale();
|
|
496
|
+
}
|
|
497
|
+
function setLocale(locale) {
|
|
498
|
+
if (!supportedLocales.includes(locale)) {
|
|
499
|
+
throw new Error(`Unsupported locale: ${locale}. Supported: ${supportedLocales.join(", ")}`);
|
|
500
|
+
}
|
|
501
|
+
currentLocale = locale;
|
|
502
|
+
}
|
|
503
|
+
function getSupportedLocales() {
|
|
504
|
+
return [...supportedLocales];
|
|
505
|
+
}
|
|
506
|
+
function getLocaleDisplayName(locale) {
|
|
507
|
+
const displayNames = {
|
|
508
|
+
tr: "T\xFCrk\xE7e",
|
|
509
|
+
en: "English",
|
|
510
|
+
de: "Deutsch",
|
|
511
|
+
fr: "Fran\xE7ais",
|
|
512
|
+
es: "Espa\xF1ol"
|
|
513
|
+
};
|
|
514
|
+
return displayNames[locale];
|
|
515
|
+
}
|
|
516
|
+
function getNestedValue(obj, path) {
|
|
517
|
+
const keys = path.split(".");
|
|
518
|
+
let value = obj;
|
|
519
|
+
for (const key of keys) {
|
|
520
|
+
if (value && typeof value === "object" && key in value) {
|
|
521
|
+
value = value[key];
|
|
522
|
+
} else {
|
|
523
|
+
return path;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return typeof value === "string" ? value : path;
|
|
527
|
+
}
|
|
528
|
+
function t(key) {
|
|
529
|
+
const locale = getLocale();
|
|
530
|
+
const translation = translations[locale];
|
|
531
|
+
const value = getNestedValue(translation, key);
|
|
532
|
+
if (value === key && locale !== "en") {
|
|
533
|
+
return getNestedValue(translations.en, key);
|
|
534
|
+
}
|
|
535
|
+
return value;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// src/config/config.ts
|
|
539
|
+
var schema = {
|
|
540
|
+
baseCurrency: {
|
|
541
|
+
type: "string",
|
|
542
|
+
default: "TRY",
|
|
543
|
+
description: "Base currency for exchange rates"
|
|
544
|
+
},
|
|
545
|
+
favorites: {
|
|
546
|
+
type: "array",
|
|
547
|
+
default: ["USD", "EUR", "GBP", "JPY"],
|
|
548
|
+
description: "Favorite currency pairs"
|
|
549
|
+
},
|
|
550
|
+
locale: {
|
|
551
|
+
type: "string",
|
|
552
|
+
default: getSystemLocale(),
|
|
553
|
+
description: "Language preference (tr, en, de, fr, es)"
|
|
554
|
+
},
|
|
555
|
+
aliases: {
|
|
556
|
+
type: "object",
|
|
557
|
+
default: {},
|
|
558
|
+
description: "Custom currency aliases"
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
var ConfigService = class {
|
|
562
|
+
store;
|
|
563
|
+
constructor() {
|
|
564
|
+
this.store = new Conf({
|
|
565
|
+
projectName: "trate",
|
|
566
|
+
schema
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
get baseCurrency() {
|
|
570
|
+
return this.store.get("baseCurrency");
|
|
571
|
+
}
|
|
572
|
+
set baseCurrency(value) {
|
|
573
|
+
this.store.set("baseCurrency", value.toUpperCase());
|
|
574
|
+
}
|
|
575
|
+
get favorites() {
|
|
576
|
+
return this.store.get("favorites");
|
|
577
|
+
}
|
|
578
|
+
set favorites(value) {
|
|
579
|
+
this.store.set(
|
|
580
|
+
"favorites",
|
|
581
|
+
value.map((v) => v.toUpperCase())
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
get locale() {
|
|
585
|
+
const stored = this.store.get("locale");
|
|
586
|
+
return stored || getSystemLocale();
|
|
587
|
+
}
|
|
588
|
+
set locale(value) {
|
|
589
|
+
this.store.set("locale", value);
|
|
590
|
+
}
|
|
591
|
+
get clientId() {
|
|
592
|
+
const stored = this.store.get("clientId");
|
|
593
|
+
if (!stored) {
|
|
594
|
+
const newId = crypto.randomUUID();
|
|
595
|
+
this.store.set("clientId", newId);
|
|
596
|
+
return newId;
|
|
597
|
+
}
|
|
598
|
+
return stored;
|
|
599
|
+
}
|
|
600
|
+
get aliases() {
|
|
601
|
+
return this.store.get("aliases") || {};
|
|
602
|
+
}
|
|
603
|
+
set aliases(value) {
|
|
604
|
+
const normalized = {};
|
|
605
|
+
for (const [key, val] of Object.entries(value)) {
|
|
606
|
+
normalized[key.toLowerCase()] = val.toUpperCase();
|
|
607
|
+
}
|
|
608
|
+
this.store.set("aliases", normalized);
|
|
609
|
+
}
|
|
610
|
+
addAlias(alias, currency) {
|
|
611
|
+
const current = this.aliases;
|
|
612
|
+
current[alias.toLowerCase()] = currency.toUpperCase();
|
|
613
|
+
this.aliases = current;
|
|
614
|
+
}
|
|
615
|
+
removeAlias(alias) {
|
|
616
|
+
const current = this.aliases;
|
|
617
|
+
if (current[alias.toLowerCase()]) {
|
|
618
|
+
delete current[alias.toLowerCase()];
|
|
619
|
+
this.aliases = current;
|
|
620
|
+
return true;
|
|
621
|
+
}
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
addFavorite(currency) {
|
|
625
|
+
const current = this.favorites;
|
|
626
|
+
if (!current.includes(currency.toUpperCase())) {
|
|
627
|
+
this.favorites = [...current, currency.toUpperCase()];
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
removeFavorite(currency) {
|
|
631
|
+
this.favorites = this.favorites.filter((f) => f !== currency.toUpperCase());
|
|
632
|
+
}
|
|
633
|
+
getAll() {
|
|
634
|
+
return {
|
|
635
|
+
baseCurrency: this.baseCurrency,
|
|
636
|
+
favorites: this.favorites,
|
|
637
|
+
locale: this.locale,
|
|
638
|
+
clientId: this.clientId,
|
|
639
|
+
aliases: this.aliases
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
reset() {
|
|
643
|
+
this.store.clear();
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
var configService = new ConfigService();
|
|
647
|
+
|
|
648
|
+
// src/convert.ts
|
|
649
|
+
import fetch from "node-fetch";
|
|
650
|
+
|
|
651
|
+
// src/utils/logger.ts
|
|
652
|
+
var LOG_LEVEL_NAMES = {
|
|
653
|
+
[0 /* DEBUG */]: "DEBUG",
|
|
654
|
+
[1 /* INFO */]: "INFO",
|
|
655
|
+
[2 /* WARN */]: "WARN",
|
|
656
|
+
[3 /* ERROR */]: "ERROR"
|
|
657
|
+
};
|
|
658
|
+
var currentLogLevel = 1 /* INFO */;
|
|
659
|
+
var isDebugMode = false;
|
|
660
|
+
function setLogLevel(level) {
|
|
661
|
+
currentLogLevel = level;
|
|
662
|
+
}
|
|
663
|
+
function setDebugMode(enabled) {
|
|
664
|
+
isDebugMode = enabled;
|
|
665
|
+
if (enabled && currentLogLevel > 0 /* DEBUG */) {
|
|
666
|
+
currentLogLevel = 0 /* DEBUG */;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
function isDebugEnabled() {
|
|
670
|
+
return isDebugMode;
|
|
671
|
+
}
|
|
672
|
+
function shouldLog(level) {
|
|
673
|
+
return level >= currentLogLevel;
|
|
674
|
+
}
|
|
675
|
+
function formatMessage(level, message, meta) {
|
|
676
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
677
|
+
const levelName = LOG_LEVEL_NAMES[level];
|
|
678
|
+
const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
|
|
679
|
+
return `[${timestamp}] [${levelName}] ${message}${metaStr}`;
|
|
680
|
+
}
|
|
681
|
+
function debug(message, meta) {
|
|
682
|
+
if (shouldLog(0 /* DEBUG */)) {
|
|
683
|
+
console.debug(formatMessage(0 /* DEBUG */, message, meta));
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function info(message, meta) {
|
|
687
|
+
if (shouldLog(1 /* INFO */)) {
|
|
688
|
+
console.info(formatMessage(1 /* INFO */, message, meta));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
function warn(message, meta) {
|
|
692
|
+
if (shouldLog(2 /* WARN */)) {
|
|
693
|
+
console.warn(formatMessage(2 /* WARN */, message, meta));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function error(message, error2, meta) {
|
|
697
|
+
if (shouldLog(3 /* ERROR */)) {
|
|
698
|
+
const errorMeta = error2 instanceof Error ? { ...meta, errorName: error2.name, errorMessage: error2.message } : { ...meta, error: error2 };
|
|
699
|
+
console.error(formatMessage(3 /* ERROR */, message, errorMeta));
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
var logger = {
|
|
703
|
+
debug,
|
|
704
|
+
info,
|
|
705
|
+
warn,
|
|
706
|
+
error,
|
|
707
|
+
setLogLevel,
|
|
708
|
+
setDebugMode,
|
|
709
|
+
isDebugEnabled
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// src/convert.ts
|
|
713
|
+
var TRATE_API = "https://trate-api.cemali.dev/v1";
|
|
714
|
+
var cache = /* @__PURE__ */ new Map();
|
|
715
|
+
var CACHE_TTL = 5 * 60 * 1e3;
|
|
716
|
+
var rateLimitMap = /* @__PURE__ */ new Map();
|
|
717
|
+
var RATE_LIMIT_WINDOW = 60 * 1e3;
|
|
718
|
+
var RATE_LIMIT_MAX = 30;
|
|
719
|
+
function checkRateLimit(endpoint) {
|
|
720
|
+
const now = Date.now();
|
|
721
|
+
const entry = rateLimitMap.get(endpoint);
|
|
722
|
+
if (!entry || now > entry.resetTime) {
|
|
723
|
+
rateLimitMap.set(endpoint, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
if (entry.count >= RATE_LIMIT_MAX) {
|
|
727
|
+
logger.warn(`Rate limit exceeded for ${endpoint}`, { resetIn: entry.resetTime - now });
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
entry.count++;
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
var cacheApi = {
|
|
734
|
+
clear: () => cache.clear()
|
|
735
|
+
};
|
|
736
|
+
var CRYPTO_SYMBOLS = [
|
|
737
|
+
"BTC",
|
|
738
|
+
"ETH",
|
|
739
|
+
"SOL",
|
|
740
|
+
"BNB",
|
|
741
|
+
"XRP",
|
|
742
|
+
"ADA",
|
|
743
|
+
"DOGE",
|
|
744
|
+
"DOT",
|
|
745
|
+
"AVAX",
|
|
746
|
+
"MATIC",
|
|
747
|
+
"LINK",
|
|
748
|
+
"UNI",
|
|
749
|
+
"LTC",
|
|
750
|
+
"ATOM"
|
|
751
|
+
];
|
|
752
|
+
var METAL_SYMBOLS = ["ONS", "GUMUS_OZ", "GRAM_ALTIN"];
|
|
753
|
+
var JEWELRY_SYMBOLS = ["CEYREK_ALTIN", "YARIM_ALTIN", "TAM_ALTIN", "ATA_ALTIN"];
|
|
754
|
+
var SUPPORTED_FIAT = [
|
|
755
|
+
"EUR",
|
|
756
|
+
"USD",
|
|
757
|
+
"GBP",
|
|
758
|
+
"JPY",
|
|
759
|
+
"CHF",
|
|
760
|
+
"CAD",
|
|
761
|
+
"AUD",
|
|
762
|
+
"NZD",
|
|
763
|
+
"CNY",
|
|
764
|
+
"HKD",
|
|
765
|
+
"SGD",
|
|
766
|
+
"INR",
|
|
767
|
+
"RUB",
|
|
768
|
+
"TRY",
|
|
769
|
+
"BRL",
|
|
770
|
+
"ZAR",
|
|
771
|
+
"MXN"
|
|
772
|
+
];
|
|
773
|
+
var JEWELRY_API_MAP = {
|
|
774
|
+
CEYREK_ALTIN: "ceyrek_altin",
|
|
775
|
+
YARIM_ALTIN: "yarim_altin",
|
|
776
|
+
TAM_ALTIN: "tam_altin",
|
|
777
|
+
ATA_ALTIN: "ata_altin"
|
|
778
|
+
};
|
|
779
|
+
var METAL_API_MAP = {
|
|
780
|
+
ONS: "ons_altin",
|
|
781
|
+
GUMUS_OZ: "gumus_ons",
|
|
782
|
+
GRAM_ALTIN: "gram_altin"
|
|
783
|
+
};
|
|
784
|
+
function isCrypto(symbol) {
|
|
785
|
+
return CRYPTO_SYMBOLS.includes(symbol.toUpperCase());
|
|
786
|
+
}
|
|
787
|
+
function isMetal(symbol) {
|
|
788
|
+
return METAL_SYMBOLS.includes(symbol.toUpperCase());
|
|
789
|
+
}
|
|
790
|
+
function isJewelry(symbol) {
|
|
791
|
+
return JEWELRY_SYMBOLS.includes(symbol.toUpperCase());
|
|
792
|
+
}
|
|
793
|
+
function isFiat(symbol) {
|
|
794
|
+
return SUPPORTED_FIAT.includes(symbol.toUpperCase());
|
|
795
|
+
}
|
|
796
|
+
function getCached(key) {
|
|
797
|
+
const cached = cache.get(key);
|
|
798
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
799
|
+
return cached.data;
|
|
800
|
+
}
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
function setCache(key, data, base) {
|
|
804
|
+
cache.set(key, { data, timestamp: Date.now(), base });
|
|
805
|
+
}
|
|
806
|
+
async function fetchWithTimeout(url, timeout = 8e3) {
|
|
807
|
+
const controller = new AbortController();
|
|
808
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
809
|
+
try {
|
|
810
|
+
const response = await fetch(url, {
|
|
811
|
+
signal: controller.signal,
|
|
812
|
+
headers: {
|
|
813
|
+
"X-Client-ID": configService.clientId,
|
|
814
|
+
"User-Agent": "trate-cli/1.0.0"
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
clearTimeout(timeoutId);
|
|
818
|
+
return response;
|
|
819
|
+
} catch (error2) {
|
|
820
|
+
clearTimeout(timeoutId);
|
|
821
|
+
throw error2;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
async function fetchFiatRates() {
|
|
825
|
+
const cacheKey = "fiat-rates";
|
|
826
|
+
const cached = getCached(cacheKey);
|
|
827
|
+
if (cached) return cached;
|
|
828
|
+
if (!checkRateLimit("fiat")) {
|
|
829
|
+
throw new Error(t("error.noConnection"));
|
|
830
|
+
}
|
|
831
|
+
try {
|
|
832
|
+
const response = await fetchWithTimeout(`${TRATE_API}/latest`);
|
|
833
|
+
if (!response.ok) throw new Error();
|
|
834
|
+
const data = await response.json();
|
|
835
|
+
if (!data.rates || Object.keys(data.rates).length === 0) throw new Error("Empty rates");
|
|
836
|
+
setCache(cacheKey, data.rates, "USD");
|
|
837
|
+
return data.rates;
|
|
838
|
+
} catch {
|
|
839
|
+
throw new Error(t("error.noConnection"));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function getFiatRateBase() {
|
|
843
|
+
const cached = cache.get("fiat-rates");
|
|
844
|
+
return cached?.base;
|
|
845
|
+
}
|
|
846
|
+
async function fetchCryptoRates() {
|
|
847
|
+
const cacheKey = "crypto-rates";
|
|
848
|
+
const cached = getCached(cacheKey);
|
|
849
|
+
if (cached) return cached;
|
|
850
|
+
if (!checkRateLimit("crypto")) {
|
|
851
|
+
throw new Error(t("error.noConnection"));
|
|
852
|
+
}
|
|
853
|
+
try {
|
|
854
|
+
const response = await fetchWithTimeout(`${TRATE_API}/crypto`);
|
|
855
|
+
if (!response.ok) throw new Error();
|
|
856
|
+
const data = await response.json();
|
|
857
|
+
setCache(cacheKey, data.prices);
|
|
858
|
+
return data.prices;
|
|
859
|
+
} catch {
|
|
860
|
+
throw new Error(t("error.noConnection"));
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
async function fetchMetalsPrices() {
|
|
864
|
+
const cacheKey = "metals-prices";
|
|
865
|
+
const cached = getCached(cacheKey);
|
|
866
|
+
if (cached) return cached;
|
|
867
|
+
if (!checkRateLimit("metals")) {
|
|
868
|
+
throw new Error(t("error.noConnection"));
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
const response = await fetchWithTimeout(`${TRATE_API}/metals`);
|
|
872
|
+
if (!response.ok) throw new Error();
|
|
873
|
+
const data = await response.json();
|
|
874
|
+
if (!data.prices || Object.keys(data.prices).length === 0) {
|
|
875
|
+
throw new Error(t("error.goldPricesUnavailable"));
|
|
876
|
+
}
|
|
877
|
+
setCache(cacheKey, data.prices);
|
|
878
|
+
return data.prices;
|
|
879
|
+
} catch (error2) {
|
|
880
|
+
if (error2 instanceof Error && error2.message.includes(t("error.goldPricesUnavailable").replace("\u015F", "s").replace("\u0131", "i"))) {
|
|
881
|
+
throw error2;
|
|
882
|
+
}
|
|
883
|
+
throw new Error(t("error.goldPricesLoadFailed"), { cause: error2 });
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
async function convert(amount, from, to) {
|
|
887
|
+
try {
|
|
888
|
+
from = from.toUpperCase();
|
|
889
|
+
to = to.toUpperCase();
|
|
890
|
+
if (from === to) {
|
|
891
|
+
return {
|
|
892
|
+
success: true,
|
|
893
|
+
result: amount,
|
|
894
|
+
rate: 1,
|
|
895
|
+
base: from,
|
|
896
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
const fromIsCrypto = isCrypto(from);
|
|
900
|
+
const toIsCrypto = isCrypto(to);
|
|
901
|
+
const fromIsMetal = isMetal(from);
|
|
902
|
+
const toIsMetal = isMetal(to);
|
|
903
|
+
const fromIsJewelry = isJewelry(from);
|
|
904
|
+
const toIsJewelry = isJewelry(to);
|
|
905
|
+
const fromIsFiat = isFiat(from);
|
|
906
|
+
const toIsFiat = isFiat(to);
|
|
907
|
+
let result;
|
|
908
|
+
let rate;
|
|
909
|
+
if (fromIsJewelry && toIsJewelry) {
|
|
910
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
911
|
+
const fromApiKey = JEWELRY_API_MAP[from];
|
|
912
|
+
const toApiKey = JEWELRY_API_MAP[to];
|
|
913
|
+
const fromPriceTRY = metalsPrices[fromApiKey].satis;
|
|
914
|
+
const toPriceTRY = metalsPrices[toApiKey].satis;
|
|
915
|
+
rate = fromPriceTRY / toPriceTRY;
|
|
916
|
+
result = amount * rate;
|
|
917
|
+
} else if (fromIsJewelry && toIsFiat) {
|
|
918
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
919
|
+
const fiatRates = await fetchFiatRates();
|
|
920
|
+
const fromApiKey = JEWELRY_API_MAP[from];
|
|
921
|
+
const jewelryPriceTRY = metalsPrices[fromApiKey].satis;
|
|
922
|
+
const jewelryPriceUSD = jewelryPriceTRY / fiatRates["TRY"];
|
|
923
|
+
if (to === "USD") {
|
|
924
|
+
rate = jewelryPriceUSD;
|
|
925
|
+
} else {
|
|
926
|
+
const targetToUSD = fiatRates[to];
|
|
927
|
+
if (!targetToUSD) {
|
|
928
|
+
return { success: false, error: `${to} ${t("error.rateNotFound")}` };
|
|
929
|
+
}
|
|
930
|
+
rate = jewelryPriceUSD * targetToUSD;
|
|
931
|
+
}
|
|
932
|
+
result = amount * rate;
|
|
933
|
+
} else if (fromIsFiat && toIsJewelry) {
|
|
934
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
935
|
+
const fiatRates = await fetchFiatRates();
|
|
936
|
+
const toApiKey = JEWELRY_API_MAP[to];
|
|
937
|
+
const jewelryPriceTRY = metalsPrices[toApiKey].satis;
|
|
938
|
+
const jewelryPriceUSD = jewelryPriceTRY / fiatRates["TRY"];
|
|
939
|
+
if (from === "USD") {
|
|
940
|
+
rate = 1 / jewelryPriceUSD;
|
|
941
|
+
} else {
|
|
942
|
+
const fiatToUSD = fiatRates[from];
|
|
943
|
+
if (!fiatToUSD) {
|
|
944
|
+
return { success: false, error: `${from} ${t("error.rateNotFound")}` };
|
|
945
|
+
}
|
|
946
|
+
rate = fiatToUSD / jewelryPriceUSD;
|
|
947
|
+
}
|
|
948
|
+
result = amount * rate;
|
|
949
|
+
} else if (fromIsJewelry && toIsCrypto) {
|
|
950
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
951
|
+
const fiatRates = await fetchFiatRates();
|
|
952
|
+
const cryptoRates = await fetchCryptoRates();
|
|
953
|
+
const fromApiKey = JEWELRY_API_MAP[from];
|
|
954
|
+
const jewelryPriceTRY = metalsPrices[fromApiKey].satis;
|
|
955
|
+
const cryptoPriceUSD = cryptoRates[to];
|
|
956
|
+
const usdToTry = fiatRates["TRY"];
|
|
957
|
+
const jewelryPriceUSD = jewelryPriceTRY / usdToTry;
|
|
958
|
+
rate = jewelryPriceUSD / cryptoPriceUSD;
|
|
959
|
+
result = amount * rate;
|
|
960
|
+
} else if (fromIsCrypto && toIsJewelry) {
|
|
961
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
962
|
+
const fiatRates = await fetchFiatRates();
|
|
963
|
+
const cryptoRates = await fetchCryptoRates();
|
|
964
|
+
const toApiKey = JEWELRY_API_MAP[to];
|
|
965
|
+
const jewelryPriceTRY = metalsPrices[toApiKey].satis;
|
|
966
|
+
const cryptoPriceUSD = cryptoRates[from];
|
|
967
|
+
const usdToTry = fiatRates["TRY"];
|
|
968
|
+
const cryptoInTRY = cryptoPriceUSD * usdToTry;
|
|
969
|
+
rate = cryptoInTRY / jewelryPriceTRY;
|
|
970
|
+
result = amount * rate;
|
|
971
|
+
} else if (fromIsMetal && toIsFiat) {
|
|
972
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
973
|
+
const fiatRates = await fetchFiatRates();
|
|
974
|
+
const apiKey = METAL_API_MAP[from];
|
|
975
|
+
let metalPriceTRY;
|
|
976
|
+
if (from === "ONS") {
|
|
977
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
978
|
+
} else if (from === "ALTIN_KG") {
|
|
979
|
+
metalPriceTRY = metalsPrices[apiKey].satis * 10.33;
|
|
980
|
+
} else {
|
|
981
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
982
|
+
}
|
|
983
|
+
const metalPriceUSD = metalPriceTRY / fiatRates["TRY"];
|
|
984
|
+
if (to === "USD") {
|
|
985
|
+
rate = metalPriceUSD;
|
|
986
|
+
} else {
|
|
987
|
+
const targetToUSD = fiatRates[to];
|
|
988
|
+
if (!targetToUSD) {
|
|
989
|
+
return { success: false, error: `${to} ${t("error.rateNotFound")}` };
|
|
990
|
+
}
|
|
991
|
+
rate = metalPriceUSD * targetToUSD;
|
|
992
|
+
}
|
|
993
|
+
result = amount * rate;
|
|
994
|
+
} else if (fromIsFiat && toIsMetal) {
|
|
995
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
996
|
+
const fiatRates = await fetchFiatRates();
|
|
997
|
+
const apiKey = METAL_API_MAP[to];
|
|
998
|
+
let metalPriceTRY;
|
|
999
|
+
if (to === "ONS") {
|
|
1000
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
1001
|
+
} else if (to === "ALTIN_KG") {
|
|
1002
|
+
metalPriceTRY = metalsPrices[apiKey].satis * 10.33;
|
|
1003
|
+
} else {
|
|
1004
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
1005
|
+
}
|
|
1006
|
+
const metalPriceUSD = metalPriceTRY / fiatRates["TRY"];
|
|
1007
|
+
if (from === "USD") {
|
|
1008
|
+
rate = 1 / metalPriceUSD;
|
|
1009
|
+
} else {
|
|
1010
|
+
const fiatToUSD = fiatRates[from];
|
|
1011
|
+
if (!fiatToUSD) {
|
|
1012
|
+
return { success: false, error: `${from} ${t("error.rateNotFound")}` };
|
|
1013
|
+
}
|
|
1014
|
+
rate = fiatToUSD / metalPriceUSD;
|
|
1015
|
+
}
|
|
1016
|
+
result = amount * rate;
|
|
1017
|
+
} else if (fromIsMetal && toIsCrypto) {
|
|
1018
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
1019
|
+
const fiatRates = await fetchFiatRates();
|
|
1020
|
+
const cryptoRates = await fetchCryptoRates();
|
|
1021
|
+
const apiKey = METAL_API_MAP[from];
|
|
1022
|
+
let metalPriceTRY;
|
|
1023
|
+
if (from === "ONS") {
|
|
1024
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
1025
|
+
} else if (from === "ALTIN_KG") {
|
|
1026
|
+
metalPriceTRY = metalsPrices[apiKey].satis * 10.33;
|
|
1027
|
+
} else {
|
|
1028
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
1029
|
+
}
|
|
1030
|
+
const metalPriceUSD = metalPriceTRY / fiatRates["TRY"];
|
|
1031
|
+
const cryptoPriceUSD = cryptoRates[to];
|
|
1032
|
+
rate = metalPriceUSD / cryptoPriceUSD;
|
|
1033
|
+
result = amount * rate;
|
|
1034
|
+
} else if (fromIsCrypto && toIsMetal) {
|
|
1035
|
+
const metalsPrices = await fetchMetalsPrices();
|
|
1036
|
+
const fiatRates = await fetchFiatRates();
|
|
1037
|
+
const cryptoRates = await fetchCryptoRates();
|
|
1038
|
+
const apiKey = METAL_API_MAP[to];
|
|
1039
|
+
let metalPriceTRY;
|
|
1040
|
+
if (to === "ONS") {
|
|
1041
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
1042
|
+
} else if (to === "ALTIN_KG") {
|
|
1043
|
+
metalPriceTRY = metalsPrices[apiKey].satis * 10.33;
|
|
1044
|
+
} else {
|
|
1045
|
+
metalPriceTRY = metalsPrices[apiKey].satis;
|
|
1046
|
+
}
|
|
1047
|
+
const cryptoPriceUSD = cryptoRates[from];
|
|
1048
|
+
const cryptoInTRY = cryptoPriceUSD * fiatRates["TRY"];
|
|
1049
|
+
rate = cryptoInTRY / metalPriceTRY;
|
|
1050
|
+
result = amount * rate;
|
|
1051
|
+
} else if (fromIsCrypto && toIsCrypto) {
|
|
1052
|
+
const cryptoRates = await fetchCryptoRates();
|
|
1053
|
+
const fromPrice = cryptoRates[from];
|
|
1054
|
+
const toPrice = cryptoRates[to];
|
|
1055
|
+
if (!fromPrice || !toPrice) {
|
|
1056
|
+
return { success: false, error: `${!fromPrice ? from : to} ${t("error.notFound")}` };
|
|
1057
|
+
}
|
|
1058
|
+
rate = fromPrice / toPrice;
|
|
1059
|
+
result = amount * rate;
|
|
1060
|
+
} else if (fromIsCrypto && toIsFiat) {
|
|
1061
|
+
const cryptoRates = await fetchCryptoRates();
|
|
1062
|
+
const fiatRates = await fetchFiatRates();
|
|
1063
|
+
const cryptoPriceUSD = cryptoRates[from];
|
|
1064
|
+
if (!cryptoPriceUSD) {
|
|
1065
|
+
return { success: false, error: `${from} ${t("error.notFound")}` };
|
|
1066
|
+
}
|
|
1067
|
+
if (to === "USD") {
|
|
1068
|
+
rate = cryptoPriceUSD;
|
|
1069
|
+
} else {
|
|
1070
|
+
const targetToUSD = fiatRates[to];
|
|
1071
|
+
if (!targetToUSD) {
|
|
1072
|
+
return { success: false, error: `${to} ${t("error.rateNotFound")}` };
|
|
1073
|
+
}
|
|
1074
|
+
rate = cryptoPriceUSD * targetToUSD;
|
|
1075
|
+
}
|
|
1076
|
+
result = amount * rate;
|
|
1077
|
+
} else if (fromIsFiat && toIsCrypto) {
|
|
1078
|
+
const cryptoRates = await fetchCryptoRates();
|
|
1079
|
+
const fiatRates = await fetchFiatRates();
|
|
1080
|
+
const cryptoPriceUSD = cryptoRates[to];
|
|
1081
|
+
if (!cryptoPriceUSD) {
|
|
1082
|
+
return { success: false, error: `${to} ${t("error.notFound")}` };
|
|
1083
|
+
}
|
|
1084
|
+
if (from === "USD") {
|
|
1085
|
+
rate = 1 / cryptoPriceUSD;
|
|
1086
|
+
} else {
|
|
1087
|
+
const fiatToUSD = fiatRates[from];
|
|
1088
|
+
if (!fiatToUSD) {
|
|
1089
|
+
return { success: false, error: `${from} ${t("error.rateNotFound")}` };
|
|
1090
|
+
}
|
|
1091
|
+
rate = fiatToUSD / cryptoPriceUSD;
|
|
1092
|
+
}
|
|
1093
|
+
result = amount * rate;
|
|
1094
|
+
} else {
|
|
1095
|
+
const fiatRates = await fetchFiatRates();
|
|
1096
|
+
const rateBase = getFiatRateBase();
|
|
1097
|
+
const fromRate = fiatRates[from];
|
|
1098
|
+
const toRate = fiatRates[to];
|
|
1099
|
+
if (!fromRate) {
|
|
1100
|
+
return { success: false, error: `${from} ${t("error.rateNotFound")}` };
|
|
1101
|
+
}
|
|
1102
|
+
if (!toRate) {
|
|
1103
|
+
return { success: false, error: `${to} ${t("error.rateNotFound")}` };
|
|
1104
|
+
}
|
|
1105
|
+
if (rateBase === "USD") {
|
|
1106
|
+
rate = toRate / fromRate;
|
|
1107
|
+
} else {
|
|
1108
|
+
rate = fromRate / toRate;
|
|
1109
|
+
}
|
|
1110
|
+
result = amount * rate;
|
|
1111
|
+
}
|
|
1112
|
+
return {
|
|
1113
|
+
success: true,
|
|
1114
|
+
result: result < 0.01 ? parseFloat(result.toFixed(8)) : Math.round(result * 100) / 100,
|
|
1115
|
+
rate: result < 0.01 ? parseFloat(rate.toPrecision(6)) : Math.round(rate * 1e6) / 1e6,
|
|
1116
|
+
base: configService.baseCurrency,
|
|
1117
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
1118
|
+
};
|
|
1119
|
+
} catch (error2) {
|
|
1120
|
+
return {
|
|
1121
|
+
success: false,
|
|
1122
|
+
error: error2 instanceof Error ? error2.message : t("error.noConnection")
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function getFiatList() {
|
|
1127
|
+
return SUPPORTED_FIAT;
|
|
1128
|
+
}
|
|
1129
|
+
function getCryptoList() {
|
|
1130
|
+
return CRYPTO_SYMBOLS;
|
|
1131
|
+
}
|
|
1132
|
+
function getMetalList() {
|
|
1133
|
+
return METAL_SYMBOLS;
|
|
1134
|
+
}
|
|
1135
|
+
function getJewelryList() {
|
|
1136
|
+
return JEWELRY_SYMBOLS;
|
|
1137
|
+
}
|
|
1138
|
+
function getDisplayName(symbol) {
|
|
1139
|
+
const displayNames = {
|
|
1140
|
+
ONS: t("display.goldOunce"),
|
|
1141
|
+
GUMUS_OZ: t("display.silverOunce"),
|
|
1142
|
+
GRAM_ALTIN: t("display.goldKg"),
|
|
1143
|
+
CEYREK_ALTIN: t("display.quarterGold"),
|
|
1144
|
+
YARIM_ALTIN: t("display.halfGold"),
|
|
1145
|
+
TAM_ALTIN: t("display.fullGold"),
|
|
1146
|
+
ATA_ALTIN: t("display.ataGold")
|
|
1147
|
+
};
|
|
1148
|
+
return displayNames[symbol.toUpperCase()] || symbol;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// src/ui.ts
|
|
1152
|
+
var COLORS = {
|
|
1153
|
+
reset: "\x1B[0m",
|
|
1154
|
+
bold: "\x1B[1m",
|
|
1155
|
+
dim: "\x1B[2m",
|
|
1156
|
+
cyan: "\x1B[36m",
|
|
1157
|
+
green: "\x1B[32m",
|
|
1158
|
+
yellow: "\x1B[33m",
|
|
1159
|
+
red: "\x1B[31m",
|
|
1160
|
+
magenta: "\x1B[35m"
|
|
1161
|
+
};
|
|
1162
|
+
var colors = {
|
|
1163
|
+
bold: (text) => `${COLORS.bold}${text}${COLORS.reset}`,
|
|
1164
|
+
dim: (text) => `${COLORS.dim}${text}${COLORS.reset}`,
|
|
1165
|
+
cyan: (text) => `${COLORS.cyan}${text}${COLORS.reset}`,
|
|
1166
|
+
green: (text) => `${COLORS.green}${text}${COLORS.reset}`,
|
|
1167
|
+
yellow: (text) => `${COLORS.yellow}${text}${COLORS.reset}`,
|
|
1168
|
+
red: (text) => `${COLORS.red}${text}${COLORS.reset}`,
|
|
1169
|
+
magenta: (text) => `${COLORS.magenta}${text}${COLORS.reset}`
|
|
1170
|
+
};
|
|
1171
|
+
function divider(width = 55) {
|
|
1172
|
+
return colors.dim("\u2500".repeat(width));
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// src/logo.ts
|
|
1176
|
+
import figlet from "figlet";
|
|
1177
|
+
var cachedLogo = null;
|
|
1178
|
+
function getLogo() {
|
|
1179
|
+
if (cachedLogo) return cachedLogo;
|
|
1180
|
+
const dollarSign = figlet.textSync("$", {
|
|
1181
|
+
font: "Slant",
|
|
1182
|
+
horizontalLayout: "default",
|
|
1183
|
+
verticalLayout: "default"
|
|
1184
|
+
});
|
|
1185
|
+
const trateText = figlet.textSync("trate", {
|
|
1186
|
+
font: "Slant",
|
|
1187
|
+
horizontalLayout: "default",
|
|
1188
|
+
verticalLayout: "default"
|
|
1189
|
+
});
|
|
1190
|
+
const white = "\x1B[38;5;15m";
|
|
1191
|
+
const reset = "\x1B[0m";
|
|
1192
|
+
const dollarLines = dollarSign.split("\n");
|
|
1193
|
+
const trateLines = trateText.split("\n");
|
|
1194
|
+
const combined = dollarLines.map((line, i) => {
|
|
1195
|
+
const trateLine = trateLines[i] || "";
|
|
1196
|
+
return `${white}${line}${reset} ${white}${trateLine}${reset}`;
|
|
1197
|
+
});
|
|
1198
|
+
cachedLogo = combined.join("\n");
|
|
1199
|
+
return cachedLogo;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/dashboard.ts
|
|
1203
|
+
async function displayFavoritesDashboard() {
|
|
1204
|
+
const config = configService.getAll();
|
|
1205
|
+
const favorites = config.favorites;
|
|
1206
|
+
const base = config.baseCurrency;
|
|
1207
|
+
const lines = [];
|
|
1208
|
+
lines.push(`
|
|
1209
|
+
${colors.bold(t("dashboard.title"))} ${t("ui.base")}: ${colors.cyan(base)}`);
|
|
1210
|
+
lines.push(colors.dim(" " + "\u2500".repeat(70)));
|
|
1211
|
+
const header = ` ${colors.bold(t("ui.symbol"))} ${colors.bold(t("ui.price"))} ${colors.bold(t("ui.type"))}`;
|
|
1212
|
+
lines.push(header);
|
|
1213
|
+
lines.push(colors.dim(" " + "\u2500".repeat(70)));
|
|
1214
|
+
const promises = favorites.map(async (symbol) => {
|
|
1215
|
+
const isCryptoSymbol = isCrypto(symbol);
|
|
1216
|
+
const isMetalSymbol = isMetal(symbol);
|
|
1217
|
+
const isJewelrySymbol = isJewelry(symbol);
|
|
1218
|
+
const rate = await convert(1, symbol, base);
|
|
1219
|
+
if (rate.success) {
|
|
1220
|
+
const displaySymbol = isMetalSymbol || isJewelrySymbol ? getDisplayName(symbol) : symbol;
|
|
1221
|
+
const resultStr = rate.result < 0.01 ? rate.result.toPrecision(6) : rate.result.toLocaleString();
|
|
1222
|
+
let typeLabel = t("dashboard.type.fiat");
|
|
1223
|
+
let symbolColor = colors.cyan(symbol);
|
|
1224
|
+
if (isCryptoSymbol) {
|
|
1225
|
+
typeLabel = t("dashboard.type.crypto");
|
|
1226
|
+
symbolColor = colors.yellow(symbol);
|
|
1227
|
+
} else if (isMetalSymbol) {
|
|
1228
|
+
typeLabel = t("dashboard.type.metal");
|
|
1229
|
+
symbolColor = colors.magenta(symbol);
|
|
1230
|
+
} else if (isJewelrySymbol) {
|
|
1231
|
+
typeLabel = t("dashboard.type.gold");
|
|
1232
|
+
symbolColor = colors.magenta(symbol + " \u{1F3C6}");
|
|
1233
|
+
}
|
|
1234
|
+
return {
|
|
1235
|
+
symbol: displaySymbol,
|
|
1236
|
+
type: typeLabel,
|
|
1237
|
+
symbolColor,
|
|
1238
|
+
price: resultStr,
|
|
1239
|
+
rate: `1 ${symbol}`
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
return null;
|
|
1243
|
+
});
|
|
1244
|
+
const results = await Promise.all(promises);
|
|
1245
|
+
for (const result of results) {
|
|
1246
|
+
if (result) {
|
|
1247
|
+
lines.push(
|
|
1248
|
+
` ${result.symbolColor.padEnd(14)} ${result.rate.padEnd(8)} ${base.padEnd(12)} ${colors.dim(result.type)}`
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
lines.push(colors.dim(" " + "\u2500".repeat(70)));
|
|
1253
|
+
lines.push(
|
|
1254
|
+
` ${colors.dim(t("ui.tip") + ":")} Use ${colors.cyan("trate <symbol>")} ${t("dashboard.usage").toLowerCase()}`
|
|
1255
|
+
);
|
|
1256
|
+
lines.push(
|
|
1257
|
+
` ${colors.dim(t("ui.tip") + ":")} Use ${colors.cyan("trate add <currency>")} ${t("dashboard.usageAdd").toLowerCase()}`
|
|
1258
|
+
);
|
|
1259
|
+
return lines.join("\n");
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// src/index.ts
|
|
1263
|
+
var STATUS_WIDTH = 55;
|
|
1264
|
+
var getSupportedCurrencies = () => {
|
|
1265
|
+
const fiat = getFiatList().join(", ");
|
|
1266
|
+
const crypto2 = getCryptoList().join(", ");
|
|
1267
|
+
const metals = getMetalList().join(", ");
|
|
1268
|
+
return { fiat, crypto: crypto2, metals };
|
|
1269
|
+
};
|
|
1270
|
+
var helpText = async () => {
|
|
1271
|
+
const config = configService.getAll();
|
|
1272
|
+
const { crypto: crypto2, metals } = getSupportedCurrencies();
|
|
1273
|
+
let statusLine = `${colors.yellow(t("ui.base") + ":")} ${colors.cyan(config.baseCurrency)}`;
|
|
1274
|
+
try {
|
|
1275
|
+
const usdResult = await convert(1, "USD", config.baseCurrency);
|
|
1276
|
+
if (usdResult.success) {
|
|
1277
|
+
statusLine += ` | 1 USD = ${colors.green(usdResult.result.toFixed(2))} ${config.baseCurrency}`;
|
|
1278
|
+
}
|
|
1279
|
+
} catch {
|
|
1280
|
+
statusLine += ` | ${colors.dim(t("ui.loading"))}`;
|
|
1281
|
+
}
|
|
1282
|
+
return `
|
|
1283
|
+
${getLogo()}
|
|
1284
|
+
|
|
1285
|
+
${colors.dim(t("app.tagline"))}
|
|
1286
|
+
|
|
1287
|
+
${colors.bold(t("help.usage") + ":")}
|
|
1288
|
+
${colors.cyan("trate <amount> <from> <to>")} ${t("help.examples")} (${colors.dim("trate 100 usd try")})
|
|
1289
|
+
${colors.cyan("trate <currency>")} ${t("dashboard.usage")} (${colors.dim("trate btc")})
|
|
1290
|
+
|
|
1291
|
+
${colors.bold(t("help.examples") + ":")}
|
|
1292
|
+
${colors.dim("$")} ${colors.yellow("trate 100 usd")} 100 USD \u2192 ${config.baseCurrency}
|
|
1293
|
+
${colors.dim("$")} ${colors.yellow("trate btc")} BTC \u2192 ${config.baseCurrency}
|
|
1294
|
+
${colors.dim("$")} ${colors.yellow("trate ceyrek_altin")} \xC7eyrek Alt\u0131n \u2192 ${config.baseCurrency}
|
|
1295
|
+
|
|
1296
|
+
${colors.bold(t("help.crypto") + ":")} ${colors.dim(crypto2)}
|
|
1297
|
+
|
|
1298
|
+
${colors.bold(t("help.metals") + ":")} ${colors.yellow(metals)}
|
|
1299
|
+
|
|
1300
|
+
${colors.bold(t("help.jewelry") + ":")}
|
|
1301
|
+
${colors.yellow("ceyrek_altin")} ${colors.yellow("yarim_altin")} ${colors.yellow("tam_altin")} ${colors.yellow("ata_altin")}
|
|
1302
|
+
|
|
1303
|
+
${colors.bold(t("help.config") + ":")}
|
|
1304
|
+
${colors.yellow("set-base")} | ${colors.yellow("list")} | ${colors.yellow("add")} | ${colors.yellow("remove")} | ${colors.yellow("set-lang")} | ${colors.yellow("alias")} | ${colors.yellow("help")}
|
|
1305
|
+
|
|
1306
|
+
${colors.bold(t("help.alias") + ":")}
|
|
1307
|
+
${colors.dim("$")} ${colors.cyan("trate alias add ceyrek CEYREK_ALTIN")} ${t("help.aliasCreate")}
|
|
1308
|
+
${colors.dim("$")} ${colors.cyan("trate alias list")} ${t("help.aliasView")}
|
|
1309
|
+
|
|
1310
|
+
${divider(STATUS_WIDTH)}
|
|
1311
|
+
${statusLine}
|
|
1312
|
+
`;
|
|
1313
|
+
};
|
|
1314
|
+
var startScreen = () => {
|
|
1315
|
+
const config = configService.getAll();
|
|
1316
|
+
return `
|
|
1317
|
+
${getLogo()}
|
|
1318
|
+
|
|
1319
|
+
${colors.dim(t("app.tagline"))}
|
|
1320
|
+
|
|
1321
|
+
${colors.dim("$")} ${colors.cyan("trate 100 usd")} 100 USD \u2192 ${config.baseCurrency}
|
|
1322
|
+
${colors.dim("$")} ${colors.cyan("trate btc")} BTC \u2192 ${config.baseCurrency}
|
|
1323
|
+
${colors.dim("$")} ${colors.cyan("trate ceyrek_altin")} \xC7eyrek Alt\u0131n
|
|
1324
|
+
${colors.dim("$")} ${colors.cyan("trate list")} ${t("dashboard.favorites")}
|
|
1325
|
+
${colors.dim("$")} ${colors.cyan("trate alias list")} View aliases
|
|
1326
|
+
${colors.dim("$")} ${colors.cyan("trate help")} ${t("commands.help")}
|
|
1327
|
+
`;
|
|
1328
|
+
};
|
|
1329
|
+
var validateCurrency = (symbol) => {
|
|
1330
|
+
const fiatList = getFiatList().map((s) => s.toUpperCase());
|
|
1331
|
+
const cryptoList = getCryptoList().map((s) => s.toUpperCase());
|
|
1332
|
+
const metalList = getMetalList().map((s) => s.toUpperCase());
|
|
1333
|
+
const jewelryList = getJewelryList().map((s) => s.toUpperCase());
|
|
1334
|
+
const upperSymbol = symbol.toUpperCase();
|
|
1335
|
+
const lowerSymbol = symbol.toLowerCase();
|
|
1336
|
+
const aliases = configService.aliases;
|
|
1337
|
+
return fiatList.includes(upperSymbol) || cryptoList.includes(upperSymbol) || metalList.includes(upperSymbol) || jewelryList.includes(upperSymbol) || lowerSymbol in aliases || Object.values(aliases).includes(upperSymbol);
|
|
1338
|
+
};
|
|
1339
|
+
var resolveAlias = (symbol) => {
|
|
1340
|
+
const aliases = configService.aliases;
|
|
1341
|
+
const lowerSymbol = symbol.toLowerCase();
|
|
1342
|
+
return aliases[lowerSymbol] || symbol.toUpperCase();
|
|
1343
|
+
};
|
|
1344
|
+
var convertCommand = async (args) => {
|
|
1345
|
+
const config = configService.getAll();
|
|
1346
|
+
let amount = 1;
|
|
1347
|
+
let from = "";
|
|
1348
|
+
let to = config.baseCurrency;
|
|
1349
|
+
if (args.length === 1) {
|
|
1350
|
+
from = resolveAlias(args[0]);
|
|
1351
|
+
to = config.baseCurrency;
|
|
1352
|
+
amount = 1;
|
|
1353
|
+
} else if (args.length === 2) {
|
|
1354
|
+
const first = args[0];
|
|
1355
|
+
const second = args[1];
|
|
1356
|
+
if (!isNaN(parseFloat(first))) {
|
|
1357
|
+
amount = parseFloat(first);
|
|
1358
|
+
from = resolveAlias(second);
|
|
1359
|
+
to = config.baseCurrency;
|
|
1360
|
+
} else {
|
|
1361
|
+
from = resolveAlias(first);
|
|
1362
|
+
to = resolveAlias(second);
|
|
1363
|
+
}
|
|
1364
|
+
} else if (args.length >= 3) {
|
|
1365
|
+
amount = parseFloat(args[0]);
|
|
1366
|
+
from = resolveAlias(args[1]);
|
|
1367
|
+
to = resolveAlias(args[2]);
|
|
1368
|
+
}
|
|
1369
|
+
if (from === to) {
|
|
1370
|
+
console.log(`
|
|
1371
|
+
${colors.dim(t("ui.info") + ":")} ${t("error.sameCurrency")}`);
|
|
1372
|
+
console.log(` ${amount} ${from} = ${amount} ${to} ${colors.dim(t("ui.noConversionNeeded"))}`);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
const result = await convert(amount, from, to);
|
|
1376
|
+
if (result.success) {
|
|
1377
|
+
const displayFrom = isMetal(from) || isJewelry(from) ? getDisplayName(from) : from;
|
|
1378
|
+
const displayTo = isMetal(to) || isJewelry(to) ? getDisplayName(to) : to;
|
|
1379
|
+
const resultStr = result.result < 0.01 ? result.result.toPrecision(6) : result.result.toLocaleString();
|
|
1380
|
+
console.log(
|
|
1381
|
+
`
|
|
1382
|
+
${colors.bold(amount.toLocaleString())} ${displayFrom} = ${colors.green(resultStr)} ${displayTo}`
|
|
1383
|
+
);
|
|
1384
|
+
console.log(
|
|
1385
|
+
` ${colors.dim(t("ui.rate") + ":")} 1 ${from} = ${result.rate < 1e-4 ? result.rate.toPrecision(6) : result.rate.toFixed(6)} ${to}`
|
|
1386
|
+
);
|
|
1387
|
+
console.log(
|
|
1388
|
+
` ${colors.dim(t("ui.base") + ":")} ${result.base} | ${colors.dim(t("ui.date") + ":")} ${result.date}`
|
|
1389
|
+
);
|
|
1390
|
+
} else {
|
|
1391
|
+
console.log(`
|
|
1392
|
+
${colors.red("Error:")} ${result.error}`);
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
var listCommand = async () => {
|
|
1396
|
+
console.log(await displayFavoritesDashboard());
|
|
1397
|
+
};
|
|
1398
|
+
var moonCommand = async () => {
|
|
1399
|
+
console.log(`
|
|
1400
|
+
${colors.bold(t("moon.toTheMoon"))}
|
|
1401
|
+
`);
|
|
1402
|
+
console.log(` ${colors.yellow("\u{1F680}")} ${colors.bold(t("moon.goingToMoon"))}`);
|
|
1403
|
+
console.log(
|
|
1404
|
+
` ${colors.dim(t("moon.tip"))} ${colors.cyan("trate <crypto>")} ${colors.dim(t("ui.type") + ":")}`
|
|
1405
|
+
);
|
|
1406
|
+
console.log("");
|
|
1407
|
+
};
|
|
1408
|
+
function initLocale() {
|
|
1409
|
+
const configLocale = configService.locale;
|
|
1410
|
+
if (configLocale) {
|
|
1411
|
+
setLocale(configLocale);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
initLocale();
|
|
1415
|
+
program.name("trate").description(t("app.tagline")).version("1.0.0");
|
|
1416
|
+
program.argument("[args...]", "Arguments for conversion or command").action(async (args) => {
|
|
1417
|
+
if (!args || args.length === 0) {
|
|
1418
|
+
console.log(startScreen());
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
const cmd = args[0].toLowerCase();
|
|
1422
|
+
if (cmd === "set-base") {
|
|
1423
|
+
const newBase = args[1]?.toUpperCase();
|
|
1424
|
+
if (!newBase) {
|
|
1425
|
+
console.log(` ${colors.red("Error:")} ${t("cli.specifyCurrency")}`);
|
|
1426
|
+
console.log(` ${colors.dim(t("cli.usage") + ":")} trate set-base <currency>`);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
if (!validateCurrency(newBase)) {
|
|
1430
|
+
console.log(` ${colors.red("Error:")} ${t("error.invalidCurrency")}: ${newBase}`);
|
|
1431
|
+
console.log(` ${colors.dim(t("help.crypto") + ":")} ${getFiatList().join(", ")}`);
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
configService.baseCurrency = newBase;
|
|
1435
|
+
console.log(` ${colors.green("\u2713")} ${t("success.baseCurrencySet")}: ${newBase}`);
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
if (cmd === "set-lang") {
|
|
1439
|
+
const newLocale = args[1]?.toLowerCase();
|
|
1440
|
+
if (!newLocale) {
|
|
1441
|
+
console.log(` ${colors.red("Error:")} ${t("cli.specifyCurrency")}`);
|
|
1442
|
+
console.log(` ${colors.dim(t("cli.usage") + ":")} trate set-lang <locale>`);
|
|
1443
|
+
console.log(
|
|
1444
|
+
` ${colors.dim("Available:")} ${getSupportedLocales().map((l) => colors.cyan(l)).join(", ")}`
|
|
1445
|
+
);
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
if (!getSupportedLocales().includes(newLocale)) {
|
|
1449
|
+
console.log(` ${colors.red("Error:")} ${t("error.invalidCurrency")}: ${newLocale}`);
|
|
1450
|
+
console.log(
|
|
1451
|
+
` ${colors.dim("Available:")} ${getSupportedLocales().map((l) => `${colors.cyan(l)} (${getLocaleDisplayName(l)})`).join(", ")}`
|
|
1452
|
+
);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
configService.locale = newLocale;
|
|
1456
|
+
setLocale(newLocale);
|
|
1457
|
+
console.log(` ${colors.green("\u2713")} Language set to: ${getLocaleDisplayName(newLocale)}`);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
if (cmd === "add") {
|
|
1461
|
+
const cur = args[1]?.toUpperCase();
|
|
1462
|
+
if (!cur) {
|
|
1463
|
+
console.log(` ${colors.red("Error:")} ${t("cli.specifyCurrency")}`);
|
|
1464
|
+
console.log(` ${colors.dim(t("cli.usage") + ":")} trate add <currency>`);
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
if (!validateCurrency(cur)) {
|
|
1468
|
+
console.log(` ${colors.red("Error:")} ${t("error.invalidCurrency")}: ${cur}`);
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
configService.addFavorite(cur);
|
|
1472
|
+
console.log(` ${colors.green("\u2713")} ${t("success.added")}: ${cur}`);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
if (cmd === "remove") {
|
|
1476
|
+
const cur = args[1]?.toUpperCase();
|
|
1477
|
+
if (!cur) {
|
|
1478
|
+
console.log(` ${colors.red("Error:")} ${t("cli.specifyCurrency")}`);
|
|
1479
|
+
console.log(` ${colors.dim(t("cli.usage") + ":")} trate remove <currency>`);
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
configService.removeFavorite(cur);
|
|
1483
|
+
console.log(` ${colors.green("\u2713")} ${t("success.removed")}: ${cur}`);
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
if (cmd === "list" || cmd === "favs") {
|
|
1487
|
+
await listCommand();
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (cmd === "moon") {
|
|
1491
|
+
await moonCommand();
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
if (cmd === "refresh") {
|
|
1495
|
+
cacheApi.clear();
|
|
1496
|
+
console.log(` ${colors.green("\u2713")} ${t("success.cacheCleared")}`);
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
if (cmd === "reset") {
|
|
1500
|
+
configService.reset();
|
|
1501
|
+
console.log(` ${colors.green("\u2713")} ${t("success.settingsReset")}`);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
if (cmd === "help") {
|
|
1505
|
+
console.log(await helpText());
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
if (cmd === "alias") {
|
|
1509
|
+
const subCmd = args[1]?.toLowerCase();
|
|
1510
|
+
const alias = args[2]?.toLowerCase();
|
|
1511
|
+
const currency = args[3]?.toUpperCase();
|
|
1512
|
+
if (subCmd === "list") {
|
|
1513
|
+
const aliases = configService.aliases;
|
|
1514
|
+
const entries = Object.entries(aliases);
|
|
1515
|
+
if (entries.length === 0) {
|
|
1516
|
+
console.log(`
|
|
1517
|
+
${colors.dim("No aliases defined")}`);
|
|
1518
|
+
} else {
|
|
1519
|
+
console.log(`
|
|
1520
|
+
${colors.bold("Aliases:")}`);
|
|
1521
|
+
for (const [key, val] of entries) {
|
|
1522
|
+
console.log(` ${colors.yellow(key)} \u2192 ${colors.cyan(val)}`);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
if (subCmd === "add") {
|
|
1528
|
+
if (!alias || !currency) {
|
|
1529
|
+
console.log(` ${colors.red("Error:")} Specify alias and currency`);
|
|
1530
|
+
console.log(
|
|
1531
|
+
` ${colors.dim("Usage:")} ${colors.cyan("trate alias add <alias> <currency>")}`
|
|
1532
|
+
);
|
|
1533
|
+
console.log(
|
|
1534
|
+
` ${colors.dim("Example:")} ${colors.cyan("trate alias add ceyrek CEYREK_ALTIN")}`
|
|
1535
|
+
);
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
const resolvedCurrency = resolveAlias(currency);
|
|
1539
|
+
if (!validateCurrency(resolvedCurrency)) {
|
|
1540
|
+
console.log(` ${colors.red("Error:")} Invalid currency: ${resolvedCurrency}`);
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
configService.addAlias(alias, resolvedCurrency);
|
|
1544
|
+
console.log(
|
|
1545
|
+
` ${colors.green("\u2713")} Alias added: ${colors.yellow(alias)} \u2192 ${colors.cyan(resolvedCurrency)}`
|
|
1546
|
+
);
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
if (subCmd === "remove") {
|
|
1550
|
+
if (!alias) {
|
|
1551
|
+
console.log(` ${colors.red("Error:")} Specify alias to remove`);
|
|
1552
|
+
console.log(` ${colors.dim("Usage:")} ${colors.cyan("trate alias remove <alias>")}`);
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
const removed = configService.removeAlias(alias);
|
|
1556
|
+
if (removed) {
|
|
1557
|
+
console.log(` ${colors.green("\u2713")} Alias removed: ${colors.yellow(alias)}`);
|
|
1558
|
+
} else {
|
|
1559
|
+
console.log(` ${colors.red("Error:")} Alias not found: ${colors.yellow(alias)}`);
|
|
1560
|
+
}
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
console.log(` ${colors.bold("Alias Commands:")}`);
|
|
1564
|
+
console.log(` ${colors.cyan("trate alias list")} ${colors.dim("List all aliases")}`);
|
|
1565
|
+
console.log(
|
|
1566
|
+
` ${colors.cyan("trate alias add <alias> <currency>")} ${colors.dim("Add alias")}`
|
|
1567
|
+
);
|
|
1568
|
+
console.log(
|
|
1569
|
+
` ${colors.cyan("trate alias remove <alias>")} ${colors.dim("Remove alias")}`
|
|
1570
|
+
);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
if (validateCurrency(cmd)) {
|
|
1574
|
+
await convertCommand(args);
|
|
1575
|
+
} else {
|
|
1576
|
+
await convertCommand(args.slice(1));
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
program.parse();
|
|
1580
|
+
process.on("SIGINT", () => {
|
|
1581
|
+
logger.debug("Received SIGINT, shutting down gracefully");
|
|
1582
|
+
process.exit(0);
|
|
1583
|
+
});
|
|
1584
|
+
process.on("SIGTERM", () => {
|
|
1585
|
+
logger.debug("Received SIGTERM, shutting down gracefully");
|
|
1586
|
+
process.exit(0);
|
|
1587
|
+
});
|