-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
333 lines (271 loc) · 12.9 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/* eslint-disable max-len */
/* Requires */
const fs = require('fs');
const path = require('path');
const https = require('https');
/**
* Informações de configuração para a execução da função, carregadas a partir de um arquivo JSON.
* @type {Object}
* @property {string} name - O nome do módulo.
* @property {string} version - A versão atual do módulo.
* @property {string} description - A descrição detalhada do módulo e sua funcionalidade.
* @property {string} developer - O nome do desenvolvedor responsável pelo módulo.
* @property {string} projects - O link para o repositório do projeto no GitHub.
* @property {string} license - A licença sob a qual o módulo é distribuído.
* @property {Object} usage - Informações sobre como utilizar o módulo.
* @property {string} usage.general - Exemplo genérico de uso do módulo.
* @property {string[]} usage.examples - Exemplos específicos de utilização do módulo.
* @property {Object[]} helps - Orientações e dicas para utilizar o módulo de forma eficaz.
* @property {Object} exports - Métodos e funcionalidades exportadas pelo módulo.
* @property {Object} files - Arquivos relevantes associados ao módulo.
* @property {Object} modules - Dependências externas necessárias para o funcionamento do módulo.
* @property {Object} settings - Configurações e opções personalizáveis do módulo.
* @property {Object} functions - Funções disponíveis para interação com o módulo.
* @property {Object} parameters - Parâmetros e configurações adicionais para personalização do módulo.
* @property {Object} results - Resultados e saídas esperadas da função do módulo.
*/
let envInfo = JSON.parse(fs.readFileSync(`${__dirname}/utils.json`));
/**
* Realiza operações de pós-processamento e retorna os dados.
* @param {Object} response - Objeto contendo os resultados da operação.
* @returns {Object} O mesmo objeto de resposta recebido.
*/
function postResults(response) {
/* Verifica se pode resetar a envInfo */
if ((envInfo.settings.finish.value === true)
|| (envInfo.settings.fail.value === true
&& response.error === true
)
) {
/* setTimeout para poder retornar */
setTimeout(() => {
/* Reseta a envInfo */
envInfo.functions.reset.value();
/* Reseta conforme o tempo */
}, envInfo.settings.wait.value);
}
/* Retorna o resultado de uma função */
return response;
}
/**
* Registra um erro no objeto de informações na envInfo.
* @param {Error} error - O erro a ser registrado.
* @returns {Object} O objeto de erro formatado.
*/
function echoError(error) {
/* Determina o erro */
const myError = !(error instanceof Error) ? new Error(`Received a instance of '${typeof error}' in function 'fail', expected an instance of 'Error'.`) : error;
/* Determina o sucesso */
envInfo.results.value.nodeDetails.isError = true;
/* Determina a falha */
envInfo.results.value.nodeDetails.code = myError.code || '0';
/* Determina a mensagem de erro */
envInfo.results.value.nodeDetails.message = myError.message || 'The operation cannot be completed because an unexpected error occurred.';
/* Define se pode printar erros */
if (envInfo.settings.error.value === true) {
/* Printa o erro usando cores */
console.log('\x1b[31m', `[${path.basename(__dirname)} #${envInfo.parameters.code.value || 0}] →`, `\x1b[33m${envInfo.parameters.message.value}`);
}
/* Retorna o erro */
return envInfo.results.value;
}
/**
* Retorna o objeto de informações do envInfo.
* @returns {Object} O objeto de informações do ambiente.
*/
const ambientDetails = () => envInfo;
/**
* Retorna o conteúdo do arquivo package.json.
* @returns {Object} O conteúdo do arquivo package.json como objeto.
*/
const packageInfo = () => JSON.parse(fs.readFileSync(`${__dirname}/package.json`));
/**
* Obtém os trending topics de um determinado país ou estado.
* @param {string} [worldName=envInfo.functions.info.arguments.worldName.value] - Nome do local para obter os trendings.
* @returns {Promise<Object>} Uma Promise que resolve com os resultados da busca de Trendings.
*/
function getTrendings(
worldName = envInfo.functions.info.arguments.worldName.value,
) {
/* Define o valor padrão com base na envInfo */
const response = envInfo.parameters.stock.value;
/* Faz uma promise, pois as versões antigas do node não tinham 'await/async' */
return new Promise((resolve) => {
/* Try - Catch para caso dê um erro pior */
try {
/* Caso seja o modo teste */
if (worldName === 'TEST#TICKET' || worldName === '') {
/* Retorna os dados padrão */
resolve(response);
/* Caso seja uso real */
} else {
/* Define o país padrão */
let place = envInfo.parameters.alias.value.worldwide;
/* Verifica se o enviado existe na envInfo */
if (Object.keys(envInfo.parameters.alias.value).includes(worldName)) {
/* Define como o padrão */
place = envInfo.parameters.alias.value[worldName.toLowerCase()];
} else {
/* Ajusta a mensagem dev */
response.dev_msg = "Region not supported, check available regions at 'locales' function.";
}
/* Define as opções do request */
const options = {
hostname: 'trends24.in',
method: 'GET',
path: place,
};
/* Let para obter a chunk da requisição */
let data = '';
/* Define a requisição com os detalhes corretos */
const req = https.get(options, (res) => {
/* Insere o código de status do request */
response.code = res.statusCode;
/* Insere a mensagem de status do request */
response.explain = envInfo.parameters.codes.value[res.statusCode];
/* Insere os headers */
response.headers = res.headers;
/* Ao receber a data */
res.on('data', (chunk) => {
/* Insere a chunk junto ao resto da data */
data += chunk;
});
/* Em caso de falhas */
req.on('error', (err) => {
/* Define como erro */
response.error = true;
/* Define o código do erro */
response.code = err.code;
/* Define a mensagem do erro */
response.error_msg = err.message;
/* Finaliza a função com o resolve */
resolve(postResults(response));
});
/* Ao receber todo o HTML */
res.on('end', () => {
/* Define a RegExp */
const parseRegExp = /<a\s+href="([^"]+)"\s+class=trend-link>([^<]+)<\/a>(?:<span\s+class=tweet-count\s+data-count=(\d*)>([^<]*)<\/span>)?/gi;
/* Realiza o matchAll na string HTML */
let result = [...data.matchAll(parseRegExp)];
/* Mapeia os resultados para o formato desejado */
result = result.map((match) => ({
url: match[1],
trend: match[2],
count: match[4] || '0K',
countraw: match[3] || '0',
}));
/* Ordena os resultados por count (countraw) de forma decrescente */
result.sort((a, b) => parseInt(b.countraw, 10) - parseInt(a.countraw, 10));
/* Remove duplicados mantendo o de maior count */
result = Object.values(result.reduce((acc, item) => {
/* Se o valor do duplicado for maior que o original */
if (
!acc[item.trend]
|| parseInt(acc[item.trend].countraw, 10) < parseInt(item.countraw, 10)
) {
/* Seleciona ele como object correta */
acc[item.trend] = item;
}
/* Se não, retorna sem assimilar nada, mantendo o original */
return acc;
/* Inicializa com uma object vazia */
}, {}));
/* Define a resposta na envInfo */
response.tweet = result;
/* Insere a data do dia */
response.date = (new Date()).toLocaleString();
/* Finaliza o request e retorna o JSON */
resolve(postResults(response));
});
});
/* Em caso de falhas | Check 2 */
req.on('error', (err) => {
/* Insere como erro */
response.error = true;
/* Insere o código do erro */
response.code = err.code;
/* Insere a mensagem do erro */
response.error_msg = err.message;
/* Retorna o resultado padrão final */
resolve(postResults(response));
});
/* Finaliza a requisição, se chegar aqui */
req.end();
}
/* Caso der erro em alguma coisa, não afeta o resultado e cai no catch abaixo */
} catch (error) {
/* Define como erro */
response.error = true;
/* Define o código de erro (Node.js dessa vez) */
response.code = error.code;
/* Define a mensagem de erro (Node.js dessa vez) */
response.error_msg = error.message;
/* Retorna o resultado padrão, mas ainda tem chances de ser algo com defeito */
resolve(postResults(response));
}
});
}
/**
* Reinicia as informações do ambiente para o estado inicial e exporta de forma módular.
* @param {Object} [changeKey={}] - Objeto opcional para alterar chaves específicas no ambiente.
* @returns {Object} O objeto exportado com as funções atualizadas.
*/
function resetAmbient(
changeKey = {},
) {
/* Define o valor padrão */
let exporting = {
reset: resetAmbient,
};
/* Try-Catch para casos de erro */
try {
/* Define a envInfo padrão */
envInfo = JSON.parse(fs.readFileSync(`${__dirname}/utils.json`));
/* Caso tenha enviado uma Object customizada */
if (Object.keys(changeKey).length !== 0) {
/* Faz a listagem das keys */
Object.keys(changeKey).forEach((key) => {
/* Edita se a key existir */
if (Object.keys(envInfo).includes(key) && key !== 'developer') {
/* Baseado na enviada */
envInfo[key] = changeKey[key];
}
});
}
/* Insere a postResults na envInfo */
envInfo.functions.dump.value = postResults;
/* Insere a ambientDetails na envInfo */
envInfo.functions.ambient.value = ambientDetails;
/* Insere a echoError na envInfo */
envInfo.functions.fail.value = echoError;
/* Insere a resetAmbient na envInfo */
envInfo.functions.reset.value = resetAmbient;
/* Insere a getTrendings na envInfo */
envInfo.functions.info.value = getTrendings;
/* Insere a package.json na envInfo */
envInfo.functions.packs.value = packageInfo;
/* Define o local completo para usos externos */
envInfo.parameters.location.value = __filename;
/* Define o resultado padrão */
envInfo.results.value = envInfo.parameters.stock.value;
/* Define as funções do arquivo */
module.exports = {
[envInfo.exports.env]: envInfo.functions.ambient.value,
[envInfo.exports.fail]: envInfo.functions.fail.value,
[envInfo.exports.dump]: envInfo.functions.dump.value,
[envInfo.exports.reset]: envInfo.functions.reset.value,
[envInfo.exports.info]: envInfo.functions.info.value,
[envInfo.exports.packs]: envInfo.functions.packs.value,
};
/* Define o valor retornado */
exporting = module.exports;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
echoError(error);
}
/* Retorna o exports */
return exporting;
}
/* Roda a injeção de funções a 1° vez */
resetAmbient();