ayo-weather-cli 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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/package.json +27 -0
  3. package/src/app.js +182 -0
package/README.md ADDED
@@ -0,0 +1,16 @@
1
+ Welcome to AYO Weather CLI!
2
+
3
+ A simple command-line application for getting weather information.
4
+
5
+ Usage:
6
+ ayo-weather [options]
7
+
8
+ Options:
9
+ -c <city> Set city
10
+ -t <token> Set API token
11
+ -h Show help
12
+
13
+ Examples:
14
+ ayo-weather -c London
15
+ ayo-weather -t your_api_token
16
+ ayo-weather -h
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "ayo-weather-cli",
3
+ "version": "1.0.0",
4
+ "description": "A simple weather CLI",
5
+ "keywords": [
6
+ "weather",
7
+ "cli"
8
+ ],
9
+ "author": "Maksim Lundin",
10
+ "license": "ISC",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/ayroneugotit/ayo-weather-cli.git"
14
+ },
15
+ "main": "./src/app.js",
16
+ "bin": {
17
+ "ayo-weather": "./src/app.js"
18
+ },
19
+ "scripts": {
20
+ "start": "node ./src/app.js",
21
+ "dev": "node --watch ./src/app.js"
22
+ },
23
+ "dependencies": {
24
+ "axios": "^1.16.1",
25
+ "chalk": "^4.1.2"
26
+ }
27
+ }
package/src/app.js ADDED
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const chalk = require('chalk');
7
+ const axios = require('axios');
8
+
9
+ class Logger {
10
+ log(title, prompt = 'LOG', message = '') {
11
+ console.log(`${chalk.green(prompt.toUpperCase())}: ${title}`);
12
+ message && console.log(message);
13
+ }
14
+
15
+ error(title, prompt = 'ERROR', message = '') {
16
+ console.log(`${chalk.red(prompt.toUpperCase())}: ${title}`);
17
+ message && console.log(message);
18
+ }
19
+ }
20
+
21
+ class ArgumentsResolver {
22
+ resolve() {
23
+ const argsArray = process.argv.slice(2);
24
+ const argsMap = new Map();
25
+
26
+ if (argsArray.length === 0) return argsMap;
27
+ if (!argsArray.at(0).startsWith('-')) {
28
+ throw new Error('Invalid arguments');
29
+ }
30
+
31
+ for (let i = 0; i < argsArray.length; i++) {
32
+ const arg = argsArray.at(i);
33
+ if (arg.startsWith('-')) {
34
+ argsMap.set(arg, true);
35
+ } else {
36
+ const prevArg = argsArray.at(i - 1);
37
+ if (!prevArg.startsWith('-')) {
38
+ throw new Error('Invalid arguments');
39
+ }
40
+ argsMap.set(prevArg, arg);
41
+ }
42
+ }
43
+ return argsMap;
44
+ }
45
+ }
46
+
47
+ class Config {
48
+ path;
49
+
50
+ constructor() {
51
+ this.path = path.join(os.homedir(), 'ayo-weather-config.json');
52
+ if (!fs.existsSync(this.path)) {
53
+ this.write({});
54
+ }
55
+ }
56
+
57
+ read() {
58
+ const config = JSON.parse(fs.readFileSync(this.path));
59
+ return config;
60
+ }
61
+
62
+ write(config) {
63
+ fs.writeFileSync(this.path, JSON.stringify(config));
64
+ }
65
+
66
+ set(key, value) {
67
+ const config = this.read();
68
+ config[key] = value;
69
+ this.write(config);
70
+ }
71
+
72
+ get(key) {
73
+ const config = this.read();
74
+ return config[key];
75
+ }
76
+
77
+ clear() {
78
+ this.write({});
79
+ }
80
+ }
81
+
82
+ class WeatherService {
83
+ async getCoordinates(city, token) {
84
+ const { data } = await axios.get(
85
+ 'https://api.openweathermap.org/geo/1.0/direct',
86
+ {
87
+ params: {
88
+ q: city,
89
+ limit: 1,
90
+ appid: token,
91
+ },
92
+ },
93
+ );
94
+
95
+ if (data.length === 0)
96
+ throw new Error(`Coordinates of city: '${city}' not found`);
97
+ const { lat, lon } = data.at(0);
98
+ return { lat, lon };
99
+ }
100
+
101
+ async getWeather(city, token) {
102
+ const { lat, lon } = await this.getCoordinates(city, token);
103
+
104
+ const { data } = await axios.get(
105
+ 'https://api.openweathermap.org/data/2.5/weather',
106
+ {
107
+ params: {
108
+ lat,
109
+ lon,
110
+ units: 'metric',
111
+ appid: token,
112
+ },
113
+ },
114
+ );
115
+
116
+ const { name, main } = data;
117
+ const { temp, humidity } = main;
118
+ return `Weather in '${data.name}': temperature: ${temp}, humidity: ${humidity}`;
119
+ }
120
+ }
121
+
122
+ class App {
123
+ logger;
124
+ argumentsResolver;
125
+ config;
126
+ weatherService;
127
+
128
+ constructor(logger, argumentsResolver, config, weatherService) {
129
+ this.logger = logger;
130
+ this.argumentsResolver = argumentsResolver;
131
+ this.config = config;
132
+ this.weatherService = weatherService;
133
+ }
134
+
135
+ async run() {
136
+ try {
137
+ const args = this.argumentsResolver.resolve();
138
+ if (args.size === 0) {
139
+ const token = this.config.get('token');
140
+ const city = this.config.get('city');
141
+ if (!token) {
142
+ throw new Error('You must set token');
143
+ }
144
+ if (!city) {
145
+ throw new Error('You must set city');
146
+ }
147
+
148
+ const weather = await this.weatherService.getWeather(
149
+ city,
150
+ token,
151
+ );
152
+ this.logger.log('', 'Weather', weather);
153
+ }
154
+ if (args.get('-h') || args.get('-help')) {
155
+ const help = fs.readFileSync('./README.md').toString();
156
+ this.logger.log('', 'help', help);
157
+ }
158
+ if (args.get('-t')) {
159
+ this.config.set('token', args.get('-t'));
160
+ this.logger.log(
161
+ `Token successfully set to '${args.get('-t')}'`,
162
+ );
163
+ }
164
+ if (args.get('-c')) {
165
+ this.config.set('city', args.get('-c'));
166
+ this.logger.log(`City successfully set to '${args.get('-c')}'`);
167
+ }
168
+ if (args.get('-r') || args.get('-resoredefaults')) {
169
+ this.config.clear();
170
+ }
171
+ } catch (e) {
172
+ this.logger.error(e.message);
173
+ }
174
+ }
175
+ }
176
+
177
+ const logger = new Logger();
178
+ const argumentsResolver = new ArgumentsResolver();
179
+ const config = new Config();
180
+ const weatherService = new WeatherService();
181
+ const app = new App(logger, argumentsResolver, config, weatherService);
182
+ app.run();