@ihsandeen/aya 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +39 -0
- package/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/commands/adab.js +28 -0
- package/dist/commands/adhan.js +58 -0
- package/dist/commands/anatomy.js +29 -0
- package/dist/commands/blame.js +29 -0
- package/dist/commands/commit.js +44 -0
- package/dist/commands/diff.js +54 -0
- package/dist/commands/dua.js +34 -0
- package/dist/commands/fast.js +36 -0
- package/dist/commands/friday.js +21 -0
- package/dist/commands/hero.js +69 -0
- package/dist/commands/hijri.js +42 -0
- package/dist/commands/history.js +98 -0
- package/dist/commands/init.js +54 -0
- package/dist/commands/invest.js +26 -0
- package/dist/commands/journal.js +84 -0
- package/dist/commands/journey.js +24 -0
- package/dist/commands/lens.js +58 -0
- package/dist/commands/memorize.js +117 -0
- package/dist/commands/mirror.js +47 -0
- package/dist/commands/names.js +48 -0
- package/dist/commands/nature.js +28 -0
- package/dist/commands/nazm.js +100 -0
- package/dist/commands/parable.js +332 -0
- package/dist/commands/prayers.js +63 -0
- package/dist/commands/pull.js +28 -0
- package/dist/commands/push.js +156 -0
- package/dist/commands/qibla.js +118 -0
- package/dist/commands/repo.js +34 -0
- package/dist/commands/sabr.js +32 -0
- package/dist/commands/scene.js +54 -0
- package/dist/commands/seek.js +28 -0
- package/dist/commands/shukr.js +22 -0
- package/dist/commands/sleep.js +26 -0
- package/dist/commands/sound.js +35 -0
- package/dist/commands/status.js +109 -0
- package/dist/commands/sunnah.js +24 -0
- package/dist/commands/tafsir.js +89 -0
- package/dist/commands/tasbih.js +50 -0
- package/dist/commands/wudu.js +22 -0
- package/dist/commands/zakat.js +72 -0
- package/dist/data/commands-db.js +365 -0
- package/dist/data/events.js +105 -0
- package/dist/data/gems.js +160 -0
- package/dist/data/nak.js +616 -0
- package/dist/data/tafsir.js +157 -0
- package/dist/data/vocab.js +105 -0
- package/dist/index.js +86 -0
- package/dist/server.js +140 -0
- package/dist/utils/config.js +38 -0
- package/dist/utils/logger.js +104 -0
- package/dist/utils/printer.js +36 -0
- package/docs/index.html +1048 -0
- package/docs/repo.html +952 -0
- package/package.json +55 -0
- package/public/hero.html +285 -0
- package/public/index.html +1039 -0
- package/public/repo.html +904 -0
- package/src/commands/adab.ts +24 -0
- package/src/commands/adhan.ts +55 -0
- package/src/commands/anatomy.ts +25 -0
- package/src/commands/blame.ts +31 -0
- package/src/commands/commit.ts +42 -0
- package/src/commands/diff.ts +56 -0
- package/src/commands/dua.ts +34 -0
- package/src/commands/fast.ts +35 -0
- package/src/commands/friday.ts +17 -0
- package/src/commands/hero.ts +73 -0
- package/src/commands/hijri.ts +43 -0
- package/src/commands/history.ts +103 -0
- package/src/commands/init.ts +53 -0
- package/src/commands/invest.ts +22 -0
- package/src/commands/journal.ts +97 -0
- package/src/commands/journey.ts +20 -0
- package/src/commands/lens.ts +58 -0
- package/src/commands/memorize.ts +131 -0
- package/src/commands/mirror.ts +48 -0
- package/src/commands/names.ts +46 -0
- package/src/commands/nature.ts +24 -0
- package/src/commands/nazm.ts +102 -0
- package/src/commands/parable.ts +360 -0
- package/src/commands/prayers.ts +65 -0
- package/src/commands/pull.ts +28 -0
- package/src/commands/push.ts +171 -0
- package/src/commands/qibla.ts +127 -0
- package/src/commands/repo.ts +34 -0
- package/src/commands/sabr.ts +28 -0
- package/src/commands/scene.ts +56 -0
- package/src/commands/seek.ts +24 -0
- package/src/commands/shukr.ts +19 -0
- package/src/commands/sleep.ts +23 -0
- package/src/commands/sound.ts +34 -0
- package/src/commands/status.ts +132 -0
- package/src/commands/sunnah.ts +21 -0
- package/src/commands/tafsir.ts +86 -0
- package/src/commands/tasbih.ts +49 -0
- package/src/commands/wudu.ts +19 -0
- package/src/commands/zakat.ts +73 -0
- package/src/data/commands-db.ts +372 -0
- package/src/data/events.ts +113 -0
- package/src/data/gems.ts +163 -0
- package/src/data/nak.ts +805 -0
- package/src/data/tafsir.ts +165 -0
- package/src/data/vocab.ts +114 -0
- package/src/index.ts +94 -0
- package/src/server.ts +128 -0
- package/src/utils/config.ts +44 -0
- package/src/utils/logger.ts +122 -0
- package/src/utils/printer.ts +38 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { getConfig } from '../utils/config';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import { printCommandHeader } from '../utils/printer';
|
|
8
|
+
|
|
9
|
+
export const qiblaCommand = new Command('qibla')
|
|
10
|
+
.description('Find the direction and distance to the Kaaba')
|
|
11
|
+
.action(async () => {
|
|
12
|
+
printCommandHeader('qibla');
|
|
13
|
+
let config = getConfig();
|
|
14
|
+
let city = config.location?.city;
|
|
15
|
+
let country = config.location?.country;
|
|
16
|
+
|
|
17
|
+
if (!city || !country) {
|
|
18
|
+
console.log(chalk.yellow('Location not configured.'));
|
|
19
|
+
const answers = await inquirer.prompt([
|
|
20
|
+
{ type: 'input', name: 'city', message: 'Enter your city:' },
|
|
21
|
+
{ type: 'input', name: 'country', message: 'Enter your country:' }
|
|
22
|
+
]);
|
|
23
|
+
city = answers.city;
|
|
24
|
+
country = answers.country;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const spinner = ora(`Locating Kaaba from ${city}, ${country}...`).start();
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// 1. Get coordinates for the city
|
|
31
|
+
const response = await axios.get('http://api.aladhan.com/v1/timingsByCity', {
|
|
32
|
+
params: { city, country }
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const meta = response.data.data.meta;
|
|
36
|
+
const lat = meta.latitude;
|
|
37
|
+
const long = meta.longitude;
|
|
38
|
+
|
|
39
|
+
// 2. Get Qibla direction
|
|
40
|
+
const qiblaResponse = await axios.get(`http://api.aladhan.com/v1/qibla/${lat}/${long}`);
|
|
41
|
+
const direction = qiblaResponse.data.data.direction; // Degrees from North
|
|
42
|
+
|
|
43
|
+
spinner.stop();
|
|
44
|
+
|
|
45
|
+
console.clear();
|
|
46
|
+
console.log(chalk.green.bold('\n Qibla Finder'));
|
|
47
|
+
console.log(chalk.gray('----------------------------------------'));
|
|
48
|
+
console.log(chalk.white(`Location: ${chalk.bold(city)}, ${country}`));
|
|
49
|
+
console.log(chalk.white(`Coordinates: ${lat}, ${long}`));
|
|
50
|
+
console.log(chalk.cyan.bold(`Qibla Direction: ${Math.round(direction)}° from North (Clockwise)`));
|
|
51
|
+
console.log('');
|
|
52
|
+
|
|
53
|
+
// Calculate Distance (Haversine Formula)
|
|
54
|
+
// Kaaba coordinates: 21.4225° N, 39.8262° E
|
|
55
|
+
const kaabaLat = 21.4225;
|
|
56
|
+
const kaabaLong = 39.8262;
|
|
57
|
+
const distance = getDistanceFromLatLonInKm(lat, long, kaabaLat, kaabaLong);
|
|
58
|
+
|
|
59
|
+
console.log(chalk.blue(`Distance to Kaaba: ${Math.round(distance).toLocaleString()} km`));
|
|
60
|
+
console.log('');
|
|
61
|
+
|
|
62
|
+
// ASCII Compass Visualization
|
|
63
|
+
drawCompass(direction);
|
|
64
|
+
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(chalk.gray('Turn your face towards the Sacred Mosque.'));
|
|
67
|
+
console.log(chalk.italic('"So turn your face toward al-Masjid al-Haram." (2:144)'));
|
|
68
|
+
|
|
69
|
+
} catch (error) {
|
|
70
|
+
spinner.fail('Could not calculate Qibla.');
|
|
71
|
+
console.error(chalk.red('Please check your internet connection or location settings.'));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
function drawCompass(degree: number) {
|
|
76
|
+
const needle = getArrow(degree);
|
|
77
|
+
const compass = `
|
|
78
|
+
N
|
|
79
|
+
|
|
|
80
|
+
W --+-- E
|
|
81
|
+
|
|
|
82
|
+
S
|
|
83
|
+
`;
|
|
84
|
+
// We can't easily rotate the whole compass in ASCII without complex logic.
|
|
85
|
+
// Instead, we show the needle direction relative to North.
|
|
86
|
+
|
|
87
|
+
console.log(chalk.yellow.bold(' N'));
|
|
88
|
+
console.log(chalk.yellow.bold(' |'));
|
|
89
|
+
|
|
90
|
+
// Show the direction arrow
|
|
91
|
+
console.log(chalk.green.bold(` ${needle} (${Math.round(degree)}°)`));
|
|
92
|
+
|
|
93
|
+
console.log(chalk.yellow.bold(' |'));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getArrow(degree: number): string {
|
|
97
|
+
const normalized = (degree % 360 + 360) % 360;
|
|
98
|
+
|
|
99
|
+
if (normalized >= 337.5 || normalized < 22.5) return '^';
|
|
100
|
+
if (normalized >= 22.5 && normalized < 67.5) return '/';
|
|
101
|
+
if (normalized >= 67.5 && normalized < 112.5) return '>';
|
|
102
|
+
if (normalized >= 112.5 && normalized < 157.5) return '\\';
|
|
103
|
+
if (normalized >= 157.5 && normalized < 202.5) return 'v';
|
|
104
|
+
if (normalized >= 202.5 && normalized < 247.5) return '/';
|
|
105
|
+
if (normalized >= 247.5 && normalized < 292.5) return '<';
|
|
106
|
+
if (normalized >= 292.5 && normalized < 337.5) return '\\';
|
|
107
|
+
|
|
108
|
+
return '^';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number) {
|
|
112
|
+
const R = 6371; // Radius of the earth in km
|
|
113
|
+
const dLat = deg2rad(lat2 - lat1); // deg2rad below
|
|
114
|
+
const dLon = deg2rad(lon2 - lon1);
|
|
115
|
+
const a =
|
|
116
|
+
Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
117
|
+
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
|
|
118
|
+
Math.sin(dLon/2) * Math.sin(dLon/2)
|
|
119
|
+
;
|
|
120
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
121
|
+
const d = R * c; // Distance in km
|
|
122
|
+
return d;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function deg2rad(deg: number) {
|
|
126
|
+
return deg * (Math.PI/180);
|
|
127
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { startServer } from '../server';
|
|
3
|
+
import { exec } from 'child_process';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { printCommandHeader } from '../utils/printer';
|
|
7
|
+
|
|
8
|
+
export const repoCommand = new Command('repo')
|
|
9
|
+
.description('The Book of Deeds UI (Visualize your journey)')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
printCommandHeader('repo');
|
|
12
|
+
|
|
13
|
+
const spinner = ora('Opening your Book of Deeds in the browser...').start();
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const port = await startServer();
|
|
17
|
+
const url = `http://localhost:${port}/repo`;
|
|
18
|
+
|
|
19
|
+
spinner.succeed(chalk.green('Book of Deeds is open!'));
|
|
20
|
+
console.log(chalk.cyan(` ➜ ${url}`));
|
|
21
|
+
console.log(chalk.dim(' (Press Ctrl+C to close the connection)'));
|
|
22
|
+
|
|
23
|
+
// Open browser based on OS
|
|
24
|
+
const start = (process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open');
|
|
25
|
+
exec(`${start} ${url}`);
|
|
26
|
+
|
|
27
|
+
// Keep the process alive is handled by the server.listen() inside startServer
|
|
28
|
+
// We don't need to do anything else, the node process will stay alive.
|
|
29
|
+
|
|
30
|
+
} catch (error) {
|
|
31
|
+
spinner.fail('Could not open Book of Deeds.');
|
|
32
|
+
console.error(error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { sabrData } from '../data/nak';
|
|
5
|
+
import { printCommandHeader } from '../utils/printer';
|
|
6
|
+
|
|
7
|
+
export const sabrCommand = new Command('sabr')
|
|
8
|
+
.description('The Patience Diagnostic')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
printCommandHeader('sabr');
|
|
11
|
+
const { situation } = await inquirer.prompt([{
|
|
12
|
+
type: 'list',
|
|
13
|
+
name: 'situation',
|
|
14
|
+
message: 'What are you struggling with?',
|
|
15
|
+
choices: sabrData.map(s => ({
|
|
16
|
+
name: `${s.type} (${s.arabic})`,
|
|
17
|
+
value: s.type
|
|
18
|
+
}))
|
|
19
|
+
}]);
|
|
20
|
+
|
|
21
|
+
const data = sabrData.find(s => s.type === situation);
|
|
22
|
+
if (data) {
|
|
23
|
+
console.log(chalk.blue.bold(`\n Diagnosis: ${data.type} (${data.arabic})`));
|
|
24
|
+
console.log(chalk.white(` Definition: ${data.description}`));
|
|
25
|
+
console.log(chalk.gray(` Example: ${data.example}`));
|
|
26
|
+
console.log(chalk.green('\n Prescription: "Indeed, Allah is with the patient." (2:153)\n'));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { sceneData } from '../data/nak';
|
|
5
|
+
import { printCommandHeader } from '../utils/printer';
|
|
6
|
+
|
|
7
|
+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
8
|
+
|
|
9
|
+
export const sceneCommand = new Command('scene')
|
|
10
|
+
.description('The Cinematic Visualizer (Tadabbur in 4K)')
|
|
11
|
+
.argument('[ref]', 'The Ayah reference (e.g. 82:1)')
|
|
12
|
+
.action(async (ref) => {
|
|
13
|
+
printCommandHeader('scene');
|
|
14
|
+
let data = ref
|
|
15
|
+
? sceneData.find(s => s.ref.includes(ref) || s.surah.toLowerCase().includes(ref))
|
|
16
|
+
: null;
|
|
17
|
+
|
|
18
|
+
if (!data && ref) {
|
|
19
|
+
console.log(chalk.red('Scene not found for that reference.'));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!data) {
|
|
23
|
+
const { choice } = await inquirer.prompt([{
|
|
24
|
+
type: 'list',
|
|
25
|
+
name: 'choice',
|
|
26
|
+
message: 'Select a scene to visualize:',
|
|
27
|
+
choices: sceneData.map(s => ({
|
|
28
|
+
name: `${s.surah} (${s.ayah}): ${s.title}`,
|
|
29
|
+
value: s
|
|
30
|
+
}))
|
|
31
|
+
}]);
|
|
32
|
+
data = choice;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!data) return;
|
|
36
|
+
|
|
37
|
+
console.clear();
|
|
38
|
+
console.log(chalk.red.bold(`\n Aya Scene: ${data.title}`));
|
|
39
|
+
console.log(chalk.gray(`Surah ${data.surah} (${data.ayah})\n`));
|
|
40
|
+
|
|
41
|
+
// Display Lighting and Audio first to set the mood
|
|
42
|
+
console.log(chalk.yellow(' Lighting: ') + chalk.white(data.lighting));
|
|
43
|
+
console.log(chalk.cyan(' Audio: ') + chalk.white(data.audio));
|
|
44
|
+
console.log(chalk.gray('----------------------------------------\n'));
|
|
45
|
+
|
|
46
|
+
console.log(chalk.white.bold(' Shot List:'));
|
|
47
|
+
|
|
48
|
+
// Animate shots sequentially
|
|
49
|
+
for (let i = 0; i < data.shots.length; i++) {
|
|
50
|
+
const shot = data.shots[i];
|
|
51
|
+
await sleep(1000);
|
|
52
|
+
console.log(chalk.green(`\n[${i+1}] ${shot.type}`));
|
|
53
|
+
console.log(chalk.white(` ${shot.description}`));
|
|
54
|
+
}
|
|
55
|
+
console.log('');
|
|
56
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { searchData } from '../data/nak';
|
|
5
|
+
import { printCommandHeader } from '../utils/printer';
|
|
6
|
+
|
|
7
|
+
export const seekCommand = new Command('seek')
|
|
8
|
+
.description('Contextual Quranic Search')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
printCommandHeader('seek');
|
|
11
|
+
const { topic } = await inquirer.prompt([{
|
|
12
|
+
type: 'list',
|
|
13
|
+
name: 'topic',
|
|
14
|
+
message: 'What topic are you looking for?',
|
|
15
|
+
choices: searchData.map(s => s.keyword)
|
|
16
|
+
}]);
|
|
17
|
+
|
|
18
|
+
const data = searchData.find(s => s.keyword === topic);
|
|
19
|
+
if (data) {
|
|
20
|
+
console.log(chalk.blue.bold(`\n Topic: ${data.keyword}`));
|
|
21
|
+
console.log(chalk.green(` Ayah: ${data.ayah}`));
|
|
22
|
+
console.log(chalk.white(` Context (The "Why"): ${data.context}\n`));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { shukrData } from '../data/nak';
|
|
4
|
+
import { printCommandHeader } from '../utils/printer';
|
|
5
|
+
|
|
6
|
+
export const shukrCommand = new Command('shukr')
|
|
7
|
+
.description('The Gratitude Reality Check')
|
|
8
|
+
.action(() => {
|
|
9
|
+
printCommandHeader('shukr');
|
|
10
|
+
console.log(chalk.yellow.bold('\n The 3 Levels of Shukr (Gratitude)\n'));
|
|
11
|
+
|
|
12
|
+
shukrData.forEach((level) => {
|
|
13
|
+
console.log(chalk.blue.bold(` Level: ${level.level}`));
|
|
14
|
+
console.log(chalk.white(` ${level.description}`));
|
|
15
|
+
console.log(chalk.green(` Reality: ${level.reality}\n`));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
console.log(chalk.gray(' "If you are grateful, I will surely increase you." (14:7)'));
|
|
19
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { sleepData } from '../data/nak';
|
|
4
|
+
import { printCommandHeader } from '../utils/printer';
|
|
5
|
+
|
|
6
|
+
export const sleepCommand = new Command('sleep')
|
|
7
|
+
.description('The Minor Death (Sleep Adhkar)')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
printCommandHeader('sleep');
|
|
10
|
+
console.clear();
|
|
11
|
+
console.log(chalk.blue.bold('\n The Minor Death (An-Nawm)\n'));
|
|
12
|
+
console.log(chalk.gray(' "Allah takes the souls at the time of their death, and those that do not die [He takes] during their sleep." (39:42)\n'));
|
|
13
|
+
|
|
14
|
+
sleepData.forEach(s => {
|
|
15
|
+
console.log(chalk.yellow.bold(` ${s.title}`));
|
|
16
|
+
console.log(chalk.green(` "${s.arabic}"`));
|
|
17
|
+
console.log(chalk.white(` "${s.translation}"`));
|
|
18
|
+
console.log(chalk.cyan.italic(` Reflection: ${s.reflection}\n`));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log(chalk.gray(' Good night. Reset your intentions for the resurrection (tomorrow).'));
|
|
22
|
+
console.log('\n');
|
|
23
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { soundData } from '../data/nak';
|
|
4
|
+
import { printCommandHeader } from '../utils/printer';
|
|
5
|
+
|
|
6
|
+
export const soundCommand = new Command('sound')
|
|
7
|
+
.description('Phonetic Storytelling (Tajweed as Tafsir)')
|
|
8
|
+
.argument('[ref]', 'The Ayah reference (e.g. 100:1)')
|
|
9
|
+
.action((ref) => {
|
|
10
|
+
printCommandHeader('sound');
|
|
11
|
+
// If no ref, show available?
|
|
12
|
+
if (!ref) {
|
|
13
|
+
console.log(chalk.yellow('Please provide an Ayah reference to analyze.'));
|
|
14
|
+
console.log(chalk.gray('Available examples: 100:1, 113:1'));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const data = soundData.find(d => d.ref === ref || `${d.surah}:${d.ayah}` === ref);
|
|
19
|
+
|
|
20
|
+
if (!data) {
|
|
21
|
+
console.log(chalk.red('Sound analysis not found for that Ayah.'));
|
|
22
|
+
console.log(chalk.gray('Currently available: 100:1 (Al-Adiyat), 113:1 (Al-Falaq)'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.clear();
|
|
27
|
+
console.log(chalk.cyan.bold(`\n Aya Sound: ${data.surah} (${data.ayah})`));
|
|
28
|
+
console.log(chalk.white.italic(`"${data.text}"`));
|
|
29
|
+
console.log(chalk.gray('----------------------------------------\n'));
|
|
30
|
+
|
|
31
|
+
console.log(chalk.green.bold('Phonetic Analysis:'));
|
|
32
|
+
console.log(chalk.white(data.analysis));
|
|
33
|
+
console.log('');
|
|
34
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { differenceInMinutes } from 'date-fns';
|
|
5
|
+
import { getConfig } from '../utils/config';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { printCommandHeader } from '../utils/printer';
|
|
8
|
+
import boxen from 'boxen';
|
|
9
|
+
|
|
10
|
+
interface PrayerTimes {
|
|
11
|
+
Fajr: string;
|
|
12
|
+
Dhuhr: string;
|
|
13
|
+
Asr: string;
|
|
14
|
+
Maghrib: string;
|
|
15
|
+
Isha: string;
|
|
16
|
+
[key: string]: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const statusCommand = new Command('status')
|
|
20
|
+
.description('Check your spiritual status (prayer times)')
|
|
21
|
+
.action(async () => {
|
|
22
|
+
printCommandHeader('status');
|
|
23
|
+
const config = getConfig();
|
|
24
|
+
const city = config.location?.city || 'Mecca';
|
|
25
|
+
const country = config.location?.country || 'Saudi Arabia';
|
|
26
|
+
|
|
27
|
+
const spinner = ora(`Pinging the heavens for ${city}, ${country}...`).start();
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const response = await axios.get('http://api.aladhan.com/v1/timingsByCity', {
|
|
31
|
+
params: {
|
|
32
|
+
city,
|
|
33
|
+
country,
|
|
34
|
+
method: 2, // ISNA
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const timings = response.data.data.timings as PrayerTimes;
|
|
39
|
+
const hijri = response.data.data.date.hijri;
|
|
40
|
+
const prayerNames = ['Fajr', 'Dhuhr', 'Asr', 'Maghrib', 'Isha'];
|
|
41
|
+
|
|
42
|
+
const now = new Date();
|
|
43
|
+
let nextPrayer = null;
|
|
44
|
+
let nextPrayerTime: Date | null = null;
|
|
45
|
+
let missedPrayersCount = 0;
|
|
46
|
+
|
|
47
|
+
// Find next prayer logic...
|
|
48
|
+
for (const name of prayerNames) {
|
|
49
|
+
const timeStr = timings[name];
|
|
50
|
+
const [hours, minutes] = timeStr.split(':').map(Number);
|
|
51
|
+
const prayerDate = new Date();
|
|
52
|
+
prayerDate.setHours(hours, minutes, 0, 0);
|
|
53
|
+
|
|
54
|
+
if (prayerDate > now) {
|
|
55
|
+
if (!nextPrayer) {
|
|
56
|
+
nextPrayer = name;
|
|
57
|
+
nextPrayerTime = prayerDate;
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
missedPrayersCount++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!nextPrayer) {
|
|
65
|
+
nextPrayer = 'Fajr';
|
|
66
|
+
const timeStr = timings['Fajr'];
|
|
67
|
+
const [hours, minutes] = timeStr.split(':').map(Number);
|
|
68
|
+
const prayerDate = new Date();
|
|
69
|
+
prayerDate.setDate(prayerDate.getDate() + 1);
|
|
70
|
+
prayerDate.setHours(hours, minutes, 0, 0);
|
|
71
|
+
nextPrayerTime = prayerDate;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
spinner.stop();
|
|
75
|
+
|
|
76
|
+
// NAK Style Status Output
|
|
77
|
+
console.log(
|
|
78
|
+
boxen(
|
|
79
|
+
chalk.bold.green(` 🕌 Spiritual Status Report `) + '\n' +
|
|
80
|
+
chalk.dim(` Location: ${city}, ${country} `) + '\n' +
|
|
81
|
+
chalk.dim(` Date: ${hijri.day} ${hijri.month.en} ${hijri.year} AH `),
|
|
82
|
+
{
|
|
83
|
+
padding: 1,
|
|
84
|
+
margin: 1,
|
|
85
|
+
borderStyle: 'round',
|
|
86
|
+
borderColor: 'green',
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
console.log(chalk.bold.blue('📡 Connection Status:'));
|
|
92
|
+
console.log(` Your heart is currently on branch ${chalk.green("'dunya'")}.`);
|
|
93
|
+
if (missedPrayersCount > 0) {
|
|
94
|
+
console.log(chalk.yellow(` Warning: ${missedPrayersCount} prayer times missed.`));
|
|
95
|
+
console.log(chalk.cyan(` Suggestion: run 'aya wudu' to refresh session.`));
|
|
96
|
+
}
|
|
97
|
+
console.log('');
|
|
98
|
+
|
|
99
|
+
if (nextPrayer && nextPrayerTime) {
|
|
100
|
+
const diff = differenceInMinutes(nextPrayerTime, now);
|
|
101
|
+
const hours = Math.floor(diff / 60);
|
|
102
|
+
const mins = diff % 60;
|
|
103
|
+
|
|
104
|
+
let timeString = '';
|
|
105
|
+
if (hours > 0) timeString += `${hours}h `;
|
|
106
|
+
timeString += `${mins}m`;
|
|
107
|
+
|
|
108
|
+
console.log(chalk.bold.yellow('⏳ Next Obligatory Commit:'));
|
|
109
|
+
console.log(` ${chalk.cyan.bold(nextPrayer)} is due in ${chalk.yellow.bold(timeString)}`);
|
|
110
|
+
console.log(chalk.italic.gray(' "Indeed, prayer has been decreed upon the believers a decree of specified times." (4:103)'));
|
|
111
|
+
}
|
|
112
|
+
console.log('');
|
|
113
|
+
|
|
114
|
+
console.log(chalk.bold.white('📅 Today\'s Schedule (The Daily Sprint):'));
|
|
115
|
+
prayerNames.forEach(name => {
|
|
116
|
+
const isNext = name === nextPrayer;
|
|
117
|
+
const color = isNext ? chalk.green.bold : chalk.white;
|
|
118
|
+
const marker = isNext ? chalk.yellow(' ← UP NEXT') : '';
|
|
119
|
+
const time = timings[name];
|
|
120
|
+
|
|
121
|
+
// Simple formatting
|
|
122
|
+
console.log(` ${color(name.padEnd(10))} ${time} ${marker}`);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(chalk.gray('(Run "aya init" to re-calibrate your location coordinates)'));
|
|
127
|
+
|
|
128
|
+
} catch (error) {
|
|
129
|
+
spinner.fail('Connection lost.');
|
|
130
|
+
console.error(chalk.red('Could not fetch prayer times. Please check your network connection.'));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { sunnahData } from '../data/nak';
|
|
4
|
+
import { printCommandHeader } from '../utils/printer';
|
|
5
|
+
|
|
6
|
+
export const sunnahCommand = new Command('sunnah')
|
|
7
|
+
.description('Daily Micro-Ibadaat Habits')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
printCommandHeader('sunnah');
|
|
10
|
+
console.clear();
|
|
11
|
+
console.log(chalk.green.bold('\n Daily Sunnah Habits (Micro-Ibadaat)\n'));
|
|
12
|
+
|
|
13
|
+
sunnahData.forEach(s => {
|
|
14
|
+
console.log(chalk.yellow.bold(` ${s.action} `) + chalk.gray(`(${s.category})`));
|
|
15
|
+
console.log(chalk.cyan(` Impact: ${s.impact}`));
|
|
16
|
+
console.log(chalk.white.italic(` ${s.description}\n`));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
console.log(chalk.gray(' "He who revives my Sunnah has loved me..."'));
|
|
20
|
+
console.log('\n');
|
|
21
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { tafsirData, Tafsir } from '../data/tafsir';
|
|
5
|
+
import { printCommandHeader } from '../utils/printer';
|
|
6
|
+
|
|
7
|
+
export const tafsirCommand = new Command('tafsir')
|
|
8
|
+
.description('Interactive linguistic deep dive (Tadabbur)')
|
|
9
|
+
.argument('[query]', 'The specific surah/ayah to study (e.g. 93:3, fatiha, ikhlas)')
|
|
10
|
+
.action(async (query) => {
|
|
11
|
+
printCommandHeader('tafsir');
|
|
12
|
+
let selectedTafsir: Tafsir | undefined;
|
|
13
|
+
|
|
14
|
+
if (query) {
|
|
15
|
+
const q = query.toLowerCase();
|
|
16
|
+
selectedTafsir = tafsirData.find(t =>
|
|
17
|
+
t.surah.toLowerCase().includes(q) ||
|
|
18
|
+
`${t.ayah}` === q ||
|
|
19
|
+
(query.includes(':') && `${t.surah}:${t.ayah}`.includes(q)) || // weak check, but okay
|
|
20
|
+
(query.includes(':') && t.ayah === parseInt(query.split(':')[1]) && t.surah.toLowerCase().includes(query.split(':')[0])) // better check? No, surah is a name in data
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Better matching logic
|
|
24
|
+
if (!selectedTafsir) {
|
|
25
|
+
// Try to match "93:3" style
|
|
26
|
+
if (query.includes(':')) {
|
|
27
|
+
const [s, a] = query.split(':');
|
|
28
|
+
// We don't have surah numbers in data, only names.
|
|
29
|
+
// But let's check if user typed "duha:3"
|
|
30
|
+
selectedTafsir = tafsirData.find(t => t.surah.toLowerCase().includes(s) && t.ayah.toString() === a);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Hardcoded fallback for specific numbers if they were in the original code
|
|
35
|
+
// 93:3 -> Ad-Duhaa
|
|
36
|
+
if (!selectedTafsir && query === '93:3') selectedTafsir = tafsirData.find(t => t.surah === 'Ad-Duhaa');
|
|
37
|
+
if (!selectedTafsir && query === '1:5') selectedTafsir = tafsirData.find(t => t.surah === 'Al-Fatiha');
|
|
38
|
+
if (!selectedTafsir && query === '103:1') selectedTafsir = tafsirData.find(t => t.surah === 'Al-Asr');
|
|
39
|
+
if (!selectedTafsir && query === '112:1') selectedTafsir = tafsirData.find(t => t.surah === 'Al-Ikhlas');
|
|
40
|
+
if (!selectedTafsir && query === '113:1') selectedTafsir = tafsirData.find(t => t.surah === 'Al-Falaq');
|
|
41
|
+
if (!selectedTafsir && query === '114:1') selectedTafsir = tafsirData.find(t => t.surah === 'An-Nas');
|
|
42
|
+
|
|
43
|
+
if (!selectedTafsir) {
|
|
44
|
+
console.log(chalk.red('Tafsir not found for that Ayah.'));
|
|
45
|
+
console.log(chalk.gray('Available: Ad-Duhaa (93:3), Al-Fatiha (1:5), Al-Asr (103:1), Al-Ikhlas (112:1), Al-Falaq (113:1), An-Nas (114:1)'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
// Interactive menu if no argument provided
|
|
50
|
+
const answers = await inquirer.prompt([
|
|
51
|
+
{
|
|
52
|
+
type: 'list',
|
|
53
|
+
name: 'selectedTafsirIndex',
|
|
54
|
+
message: 'Choose a Surah/Ayah to study:',
|
|
55
|
+
choices: tafsirData.map((t, index) => ({
|
|
56
|
+
name: `${t.surah} (Ayah ${t.ayah})`,
|
|
57
|
+
value: index
|
|
58
|
+
}))
|
|
59
|
+
}
|
|
60
|
+
]);
|
|
61
|
+
selectedTafsir = tafsirData[answers.selectedTafsirIndex];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!selectedTafsir) return;
|
|
65
|
+
|
|
66
|
+
console.clear();
|
|
67
|
+
console.log(chalk.green.bold(`\n Tafsir: Surah ${selectedTafsir.surah}, Ayah ${selectedTafsir.ayah}`));
|
|
68
|
+
console.log(chalk.gray('Take a deep breath. Let the words sink in.\n'));
|
|
69
|
+
|
|
70
|
+
for (const step of selectedTafsir.steps) {
|
|
71
|
+
await inquirer.prompt([{
|
|
72
|
+
type: 'input',
|
|
73
|
+
name: 'continue',
|
|
74
|
+
message: chalk.yellow.bold(step.word),
|
|
75
|
+
suffix: chalk.gray(' (Press Enter to reveal meaning)')
|
|
76
|
+
}]);
|
|
77
|
+
|
|
78
|
+
console.log(chalk.cyan(`Meaning: ${step.meaning}`));
|
|
79
|
+
console.log(chalk.white(step.explanation));
|
|
80
|
+
console.log(chalk.gray('----------------------------------------'));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(chalk.green.bold('\n Final Reflection:'));
|
|
84
|
+
console.log(chalk.white.italic(selectedTafsir.finalThought));
|
|
85
|
+
console.log('');
|
|
86
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { printCommandHeader } from '../utils/printer';
|
|
5
|
+
|
|
6
|
+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
7
|
+
|
|
8
|
+
export const tasbihCommand = new Command('tasbih')
|
|
9
|
+
.description('Digital Counter (Tasbih)')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
printCommandHeader('tasbih');
|
|
12
|
+
let count = 0;
|
|
13
|
+
|
|
14
|
+
console.clear();
|
|
15
|
+
console.log(chalk.cyan.bold('\n Digital Tasbih'));
|
|
16
|
+
console.log(chalk.gray('Press ENTER to count. Type "reset" to reset. Ctrl+C to exit.\n'));
|
|
17
|
+
|
|
18
|
+
// We need a loop to handle inputs
|
|
19
|
+
while (true) {
|
|
20
|
+
const { input } = await inquirer.prompt([{
|
|
21
|
+
type: 'input',
|
|
22
|
+
name: 'input',
|
|
23
|
+
message: `Count: ${chalk.green.bold(count)} Wait for prompt...`,
|
|
24
|
+
default: '',
|
|
25
|
+
prefix: '' // Remove prefix
|
|
26
|
+
}]);
|
|
27
|
+
|
|
28
|
+
if (input.toLowerCase() === 'reset') {
|
|
29
|
+
count = 0;
|
|
30
|
+
} else if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'q') {
|
|
31
|
+
break;
|
|
32
|
+
} else {
|
|
33
|
+
count++;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.clear();
|
|
37
|
+
console.log(chalk.cyan.bold('\n Digital Tasbih'));
|
|
38
|
+
console.log(chalk.gray('Press ENTER to count. Type "reset" to reset. Type "exit" to quit.\n'));
|
|
39
|
+
console.log(chalk.green.bold(`Current Count: ${count}`));
|
|
40
|
+
|
|
41
|
+
// Simple feedback
|
|
42
|
+
if (count % 33 === 0 && count > 0) {
|
|
43
|
+
console.log(chalk.yellow(' SubhanAllah / Alhamdulillah / Allahu Akbar (33 completed)'));
|
|
44
|
+
}
|
|
45
|
+
if (count % 100 === 0 && count > 0) {
|
|
46
|
+
console.log(chalk.magenta(' 100 Completed! May Allah accept it.'));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { wuduData } from '../data/nak';
|
|
4
|
+
import { printCommandHeader } from '../utils/printer';
|
|
5
|
+
|
|
6
|
+
export const wuduCommand = new Command('wudu')
|
|
7
|
+
.description('The Spiritual Detox (Washing away sins)')
|
|
8
|
+
.action(() => {
|
|
9
|
+
printCommandHeader('wudu');
|
|
10
|
+
console.log(chalk.cyan.bold('\n Wudu: The Spiritual Detox\n'));
|
|
11
|
+
|
|
12
|
+
wuduData.forEach((step, index) => {
|
|
13
|
+
console.log(chalk.blue.bold(` ${index + 1}. ${step.step}`));
|
|
14
|
+
console.log(chalk.white(` Physical: ${step.physical}`));
|
|
15
|
+
console.log(chalk.green(` Spiritual: ${step.spiritual}\n`));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
console.log(chalk.gray(' "Oh Allah, make me of those who repent and those who purify themselves."'));
|
|
19
|
+
});
|