Jonell01 commited on
Commit
93b21b9
ยท
verified ยท
1 Parent(s): 12dfcff

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +134 -164
index.js CHANGED
@@ -1,6 +1,7 @@
1
  const express = require('express');
2
  const bodyParser = require('body-parser');
3
  const axios = require('axios');
 
4
  const fs = require('fs');
5
  const path = require('path');
6
  const WebSocket = require('ws');
@@ -156,75 +157,12 @@ function updateLastSeen(category, items) {
156
  saveLastSeen(lastSeen);
157
  }
158
 
159
- async function sendWebSocketMessageToActiveSessions(stockData, weatherData) {
160
- const favorites = loadFavorites();
161
- for (const [senderId] of activeSessions.entries()) {
162
- const userFavorites = favorites[senderId] || [];
163
- let sections = [];
164
- let matchCount = 0;
165
-
166
- const checkAndAdd = (label, section, useEmoji) => {
167
- const items = section.items || [];
168
- const matchedItems = userFavorites.length > 0
169
- ? items.filter(i => userFavorites.includes(cleanText(i.name)))
170
- : items;
171
-
172
- if (userFavorites.length > 0 && matchedItems.length === 0) return false;
173
-
174
- matchCount += matchedItems.length;
175
- const formattedItems = matchedItems.map(i =>
176
- `- ${useEmoji ? addEmoji(i.name) : i.name}: ${formatValue(i.quantity)}`
177
- ).join("\n");
178
-
179
- sections.push(`${label}:\n${formattedItems}\nโณ Restock In: ${section.countdown}`);
180
- return true;
181
- };
182
-
183
- checkAndAdd("๐Ÿ› ๏ธ ๐—š๐—ฒ๐—ฎ๐—ฟ", stockData.gear, true);
184
- checkAndAdd("๐ŸŒฑ ๐—ฆ๐—ฒ๐—ฒ๐—ฑ๐˜€", stockData.seed, true);
185
- checkAndAdd("๐Ÿฅš ๐—˜๐—ด๐—ด๐˜€", stockData.egg, true);
186
- checkAndAdd("๐ŸŽจ ๐—–๐—ผ๐˜€๐—บ๐—ฒ๐˜๐—ถ๐—ฐ๐˜€", stockData.cosmetics, false);
187
- checkAndAdd("๐ŸŽ‰ ๐—˜๐˜ƒ๐—ฒ๐—ป๐˜", stockData.honey, false);
188
-
189
- if (userFavorites.length > 0 && matchCount === 0) continue;
190
- if (sections.length === 0) continue;
191
-
192
- const updatedAt = getPHTime().toLocaleString("en-PH", {
193
- hour: "numeric", minute: "numeric", second: "numeric",
194
- hour12: true, day: "2-digit", month: "short", year: "numeric"
195
- });
196
-
197
- const weatherInfo = weatherData
198
- ? `๐ŸŒค๏ธ ๐—ช๐—ฒ๐—ฎ๐˜๐—ต๐—ฒ๐—ฟ: ${weatherData.icon} ${weatherData.weatherType}\n๐Ÿ“‹ ${weatherData.description}\n๐ŸŽฏ ${weatherData.cropBonuses}\n`
199
- : "";
200
-
201
- const title = userFavorites.length > 0
202
- ? `โ™ฅ๏ธ ${matchCount} ๐—™๐—ฎ๐˜ƒ๐—ผ๐—ฟ๐—ถ๐˜๐—ฒ ๐—ถ๐˜๐—ฒ๐—บ${matchCount > 1 ? "s" : ""} ๐—™๐—ผ๐˜‚๐—ป๐—ฑ!`
203
- : "๐ŸŒพ ๐—š๐—ฟ๐—ผ๐˜„ ๐—” ๐—š๐—”๐—ฅ๐——๐—˜๐—ก โ€” ๐—ง๐—ฟ๐—ฎ๐—ฐ๐—ธ๐—ฒ๐—ฟ";
204
-
205
- const messageKey = JSON.stringify({ title, sections, weatherInfo, updatedAt });
206
- const lastSent = lastSentCache.get(senderId);
207
- if (lastSent === messageKey) continue;
208
-
209
- lastSentCache.set(senderId, messageKey);
210
-
211
- await sendMessage(senderId,
212
- `${title}\n\n${sections.join("\n\n")}\n\n${weatherInfo}๐Ÿ“… Updated at (PH): ${updatedAt}`
213
- );
214
- }
215
- }
216
-
217
  function ensureWebSocketConnection() {
218
  if (sharedWebSocket && sharedWebSocket.readyState === WebSocket.OPEN) return;
219
 
220
- if (sharedWebSocket) {
221
- sharedWebSocket.close();
222
- }
223
-
224
  sharedWebSocket = new WebSocket("wss://gagstock.gleeze.com");
225
 
226
  sharedWebSocket.on("open", () => {
227
- clearInterval(keepAliveInterval);
228
  keepAliveInterval = setInterval(() => {
229
  if (sharedWebSocket.readyState === WebSocket.OPEN) {
230
  sharedWebSocket.send("ping");
@@ -243,38 +181,88 @@ function ensureWebSocketConnection() {
243
  seed: stock.seed,
244
  egg: stock.egg,
245
  cosmetics: stock.cosmetics,
246
- honey: stock.honey,
247
  };
248
 
249
  updateLastSeen("gear", stockData.gear.items);
250
  updateLastSeen("seed", stockData.seed.items);
251
  updateLastSeen("egg", stockData.egg.items);
252
  updateLastSeen("cosmetics", stockData.cosmetics.items);
253
- updateLastSeen("event", stockData.honey.items);
254
-
255
- const weather = await axios.get("https://growagardenstock.com/api/stock/weather")
256
- .then(res => res.data)
257
- .catch(() => null);
258
-
259
- await sendWebSocketMessageToActiveSessions(stockData, weather);
260
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  } catch (error) {
262
  console.error('Error processing WebSocket message:', error);
263
  }
264
  });
265
 
266
- sharedWebSocket.on("close", (code, reason) => {
267
- console.log(`WebSocket closed: Code ${code}, Reason: ${reason}`);
268
  clearInterval(keepAliveInterval);
269
  sharedWebSocket = null;
270
  setTimeout(ensureWebSocketConnection, 3000);
271
  });
272
 
273
- sharedWebSocket.on("error", (error) => {
274
- console.error('WebSocket error:', error.message);
275
- if (sharedWebSocket) {
276
- sharedWebSocket.close();
277
- }
278
  });
279
  }
280
 
@@ -289,101 +277,91 @@ async function sendTyping(recipientId) {
289
  }
290
  }
291
 
292
- async function getStockDataViaWebSocket() {
293
- return new Promise((resolve, reject) => {
294
- if (sharedWebSocket && sharedWebSocket.readyState === WebSocket.OPEN) {
295
- const messageHandler = (data) => {
296
- try {
297
- const payload = JSON.parse(data);
298
- if (payload.status === "success" && payload.data) {
299
- sharedWebSocket.off('message', messageHandler);
300
- resolve(payload.data);
301
- }
302
- } catch (e) {
303
- console.error('Error parsing WebSocket response:', e);
304
- }
305
- };
306
- sharedWebSocket.on('message', messageHandler);
307
- sharedWebSocket.send(JSON.stringify({ request: 'stock' }));
308
- setTimeout(() => {
309
- sharedWebSocket.off('message', messageHandler);
310
- reject(new Error('WebSocket stock data request timed out'));
311
- }, 5000);
312
- } else {
313
- reject(new Error('WebSocket not open for stock data.'));
314
- }
315
- }).catch(error => {
316
- console.error('Failed to get stock data via WebSocket:', error.message);
317
  return null;
318
- });
319
  }
320
 
321
- async function getWeatherDataViaWebSocket() {
322
- return new Promise((resolve, reject) => {
323
- if (sharedWebSocket && sharedWebSocket.readyState === WebSocket.OPEN) {
324
- const messageHandler = (data) => {
325
- try {
326
- const payload = JSON.parse(data);
327
- if (payload.status === "success" && payload.data) {
328
- sharedWebSocket.off('message', messageHandler);
329
- resolve(payload.data);
330
- }
331
- } catch (e) {
332
- console.error('Error parsing WebSocket response:', e);
333
- }
334
- };
335
- sharedWebSocket.on('message', messageHandler);
336
- sharedWebSocket.send(JSON.stringify({ request: 'weather' }));
337
- setTimeout(() => {
338
- sharedWebSocket.off('message', messageHandler);
339
- reject(new Error('WebSocket weather data request timed out'));
340
- }, 5000);
341
- } else {
342
- reject(new Error('WebSocket not open for weather data.'));
343
- }
344
- }).catch(error => {
345
- console.error('Failed to get weather data via WebSocket:', error.message);
346
  return null;
347
- });
348
  }
349
 
350
- function formatStockMessage(stockData) {
351
- if (!stockData) return "โš ๏ธ Could not fetch stock data";
352
 
353
  let message = `๐Ÿ›’ ๐—–๐—จ๐—ฅ๐—ฅ๐—˜๐—ก๐—ง ๐—ฆ๐—ง๐—ข๐—–๐—ž ๐—จ๐—ฃ๐——๐—”๐—ง๐—˜๐—ฆ ๐Ÿ›’\n`;
354
- message += `๐Ÿ•’ LAST UPDATED: ${getPHTime().toLocaleString("en-PH", {
355
- hour: "numeric", minute: "numeric", second: "numeric",
356
- hour12: true, day: "2-digit", month: "short", year: "numeric"
357
- })}\n\n`;
358
 
 
359
  const categories = {
360
- gear: '๐Ÿ› ๏ธ ๐—š๐—˜๐—”๐—ฅ',
361
- seed: '๐ŸŒฑ ๐—ฆ๐—˜๐—˜๐——๐—ฆ',
362
- egg: '๐Ÿฅš ๐—˜๐—š๐—š๐—ฆ',
363
- honey: '๐Ÿฏ ๐—›๐—ข๐—ก๐—˜๐—ฌ',
364
- cosmetics: '๐Ÿ’„ ๐—–๐—ข๐—ฆ๐— ๐—˜๐—ง๐—œ๐—–๐—ฆ '
 
 
365
  };
366
 
367
  for (const [categoryKey, categoryName] of Object.entries(categories)) {
368
- if (stockData[categoryKey] && stockData[categoryKey].items && stockData[categoryKey].items.length > 0) {
369
  message += `${categoryName}:\n`;
370
- stockData[categoryKey].items.forEach(item => {
371
- message += `โ€ข ${addEmoji(item.name)}: ${formatValue(item.quantity)}\n`;
372
  });
373
- message += `โณ Restock In: ${stockData[categoryKey].countdown}\n\n`;
374
  }
375
  }
376
  return message;
377
  }
378
 
379
  function formatWeatherMessage(weatherData) {
380
- if (!weatherData) return "โš ๏ธ Could not fetch weather data";
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
  let message = 'โ›… ๐—–๐—จ๐—ฅ๐—ฅ๐—˜๐—ก๐—ง ๐—ช๐—˜๐—”๐—ง๐—›๐—˜๐—ฅ ๐—–๐—ข๐—ก๐——๐—œ๐—ง๐—œ๐—ข๐—ก๐—ฆ\n\n';
 
383
 
384
- message += `๐ŸŒค๏ธ ๐—ช๐—ฒ๐—ฎ๐˜๐—ต๐—ฒ๐—ฟ: ${weatherData.icon} ${weatherData.weatherType}\n`;
385
- message += `๐Ÿ“‹ ${weatherData.description}\n`;
386
- message += `๐ŸŽฏ ${weatherData.cropBonuses}\n`;
 
 
 
 
 
 
 
 
 
387
 
388
  return message;
389
  }
@@ -418,13 +396,9 @@ function setupUserNotification(recipientId, intervalMinutes = 5) {
418
  }
419
 
420
  const intervalId = setInterval(async () => {
421
- const stockData = await getStockDataViaWebSocket();
422
- const weatherData = await getWeatherDataViaWebSocket();
423
- if (stockData) {
424
- await sendMessage(recipientId, formatStockMessage(stockData));
425
- }
426
- if (weatherData) {
427
- await sendMessage(recipientId, formatWeatherMessage(weatherData));
428
  }
429
  }, intervalMinutes * 60 * 1000);
430
 
@@ -566,13 +540,11 @@ async function handleMessage(senderId, messageText) {
566
  await sendMessage(senderId, helpMessage);
567
  }
568
  else if (command === '/stock') {
569
- ensureWebSocketConnection();
570
- const stockData = await getStockDataViaWebSocket();
571
- await sendMessage(senderId, stockData ? formatStockMessage(stockData) : 'โš ๏ธ Could not fetch stock data');
572
  }
573
  else if (command === '/weather') {
574
- ensureWebSocketConnection();
575
- const weatherData = await getWeatherDataViaWebSocket();
576
  await sendMessage(senderId, weatherData ? formatWeatherMessage(weatherData) : 'โš ๏ธ Could not fetch weather data');
577
  }
578
  else if (command.startsWith('/gagstock')) {
@@ -587,7 +559,6 @@ async function handleMessage(senderId, messageText) {
587
  await sendMessage(senderId, 'โš ๏ธ Please provide valid minutes (e.g., /noti on 5)');
588
  return;
589
  }
590
- ensureWebSocketConnection();
591
  setupUserNotification(senderId, interval);
592
  await sendMessage(senderId, `๐Ÿ”” Notifications enabled! Updates every ${interval} minutes.`);
593
  }
@@ -605,7 +576,6 @@ async function handleMessage(senderId, messageText) {
605
  }
606
 
607
  loadUserPreferences();
608
- ensureWebSocketConnection();
609
  const PORT = process.env.PORT || 7860;
610
  app.listen(PORT, () => {
611
  console.log(`Bot running on port ${PORT}`);
 
1
  const express = require('express');
2
  const bodyParser = require('body-parser');
3
  const axios = require('axios');
4
+ const cron = require('node-cron');
5
  const fs = require('fs');
6
  const path = require('path');
7
  const WebSocket = require('ws');
 
157
  saveLastSeen(lastSeen);
158
  }
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  function ensureWebSocketConnection() {
161
  if (sharedWebSocket && sharedWebSocket.readyState === WebSocket.OPEN) return;
162
 
 
 
 
 
163
  sharedWebSocket = new WebSocket("wss://gagstock.gleeze.com");
164
 
165
  sharedWebSocket.on("open", () => {
 
166
  keepAliveInterval = setInterval(() => {
167
  if (sharedWebSocket.readyState === WebSocket.OPEN) {
168
  sharedWebSocket.send("ping");
 
181
  seed: stock.seed,
182
  egg: stock.egg,
183
  cosmetics: stock.cosmetics,
184
+ event: stock.honey,
185
  };
186
 
187
  updateLastSeen("gear", stockData.gear.items);
188
  updateLastSeen("seed", stockData.seed.items);
189
  updateLastSeen("egg", stockData.egg.items);
190
  updateLastSeen("cosmetics", stockData.cosmetics.items);
191
+ updateLastSeen("event", stockData.event.items);
192
+
193
+ const favorites = loadFavorites();
194
+
195
+ for (const [senderId] of activeSessions.entries()) {
196
+ const userFavorites = favorites[senderId] || [];
197
+ let sections = [];
198
+ let matchCount = 0;
199
+
200
+ const checkAndAdd = (label, section, useEmoji) => {
201
+ const items = section.items || [];
202
+ const matchedItems = userFavorites.length > 0
203
+ ? items.filter(i => userFavorites.includes(cleanText(i.name)))
204
+ : items;
205
+
206
+ if (userFavorites.length > 0 && matchedItems.length === 0) return false;
207
+
208
+ matchCount += matchedItems.length;
209
+ const formattedItems = matchedItems.map(i =>
210
+ `- ${useEmoji ? addEmoji(i.name) : i.name}: ${formatValue(i.quantity)}`
211
+ ).join("\n");
212
+
213
+ sections.push(`${label}:\n${formattedItems}\nโณ Restock In: ${section.countdown}`);
214
+ return true;
215
+ };
216
+
217
+ checkAndAdd("๐Ÿ› ๏ธ ๐—š๐—ฒ๐—ฎ๐—ฟ", stockData.gear, true);
218
+ checkAndAdd("๐ŸŒฑ ๐—ฆ๐—ฒ๐—ฒ๐—ฑ๐˜€", stockData.seed, true);
219
+ checkAndAdd("๐Ÿฅš ๐—˜๐—ด๐—ด๐˜€", stockData.egg, true);
220
+ checkAndAdd("๐ŸŽจ ๐—–๐—ผ๐˜€๐—บ๐—ฒ๐˜๐—ถ๐—ฐ๐˜€", stockData.cosmetics, false);
221
+ checkAndAdd("๐ŸŽ‰ ๐—˜๐˜ƒ๐—ฒ๐—ป๐˜", stockData.event, false);
222
+
223
+ if (userFavorites.length > 0 && matchCount === 0) continue;
224
+ if (sections.length === 0) continue;
225
+
226
+ const updatedAt = getPHTime().toLocaleString("en-PH", {
227
+ hour: "numeric", minute: "numeric", second: "numeric",
228
+ hour12: true, day: "2-digit", month: "short", year: "numeric"
229
+ });
230
+
231
+ const weather = await axios.get("https://growagardenstock.com/api/stock/weather")
232
+ .then(res => res.data)
233
+ .catch(() => null);
234
+
235
+ const weatherInfo = weather
236
+ ? `๐ŸŒค๏ธ ๐—ช๐—ฒ๐—ฎ๐˜๐—ต๐—ฒ๐—ฟ: ${weather.icon} ${weather.weatherType}\n๐Ÿ“‹ ${weather.description}\n๐ŸŽฏ ${weather.cropBonuses}\n`
237
+ : "";
238
+
239
+ const title = userFavorites.length > 0
240
+ ? `โ™ฅ๏ธ ${matchCount} ๐—™๐—ฎ๐˜ƒ๐—ผ๐—ฟ๐—ถ๐˜๐—ฒ ๐—ถ๐˜๐—ฒ๐—บ${matchCount > 1 ? "s" : ""} ๐—™๐—ผ๐˜‚๐—ป๐—ฑ!`
241
+ : "๐ŸŒพ ๐—š๐—ฟ๐—ผ๐˜„ ๐—” ๐—š๐—ฎ๐—ฟ๐—ฑ๐—ฒ๐—ป โ€” ๐—ง๐—ฟ๐—ฎ๐—ฐ๐—ธ๐—ฒ๐—ฟ";
242
+
243
+ const messageKey = JSON.stringify({ title, sections, weatherInfo, updatedAt });
244
+ const lastSent = lastSentCache.get(senderId);
245
+ if (lastSent === messageKey) continue;
246
+
247
+ lastSentCache.set(senderId, messageKey);
248
+
249
+ await sendMessage(senderId,
250
+ `${title}\n\n${sections.join("\n\n")}\n\n${weatherInfo}๐Ÿ“… Updated at (PH): ${updatedAt}`
251
+ );
252
+ }
253
  } catch (error) {
254
  console.error('Error processing WebSocket message:', error);
255
  }
256
  });
257
 
258
+ sharedWebSocket.on("close", () => {
 
259
  clearInterval(keepAliveInterval);
260
  sharedWebSocket = null;
261
  setTimeout(ensureWebSocketConnection, 3000);
262
  });
263
 
264
+ sharedWebSocket.on("error", () => {
265
+ sharedWebSocket?.close();
 
 
 
266
  });
267
  }
268
 
 
277
  }
278
  }
279
 
280
+ async function getStockData() {
281
+ try {
282
+ const [stockResponse, updateResponse] = await Promise.all([
283
+ axios.get('https://kenlie.top/api/gag/stocks/'),
284
+ axios.get('https://jonell01-reuploadotherhruhh.hf.space/grow')
285
+ ]);
286
+ return {
287
+ stockData: stockResponse.data,
288
+ lastUpdated: updateResponse.data.lastUpdated
289
+ };
290
+ } catch (error) {
291
+ console.error('Error fetching stock data:', error.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  return null;
293
+ }
294
  }
295
 
296
+ async function getWeatherData() {
297
+ try {
298
+ const response = await axios.get('https://kenlie.top/api/gag/weather/');
299
+ return response.data;
300
+ } catch (error) {
301
+ console.error('Error fetching weather data:', error.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  return null;
303
+ }
304
  }
305
 
306
+ function formatStockMessage(data) {
307
+ if (!data || !data.stockData.status) return "โš ๏ธ Could not fetch stock data";
308
 
309
  let message = `๐Ÿ›’ ๐—–๐—จ๐—ฅ๐—ฅ๐—˜๐—ก๐—ง ๐—ฆ๐—ง๐—ข๐—–๐—ž ๐—จ๐—ฃ๐——๐—”๐—ง๐—˜๐—ฆ ๐Ÿ›’\n`;
310
+ message += `๐Ÿ•’ LAST UPDATED: ${new Date(data.lastUpdated).toLocaleString()}\n\n`;
 
 
 
311
 
312
+ const stockData = data.stockData.response;
313
  const categories = {
314
+ gearStock: '๐Ÿ› ๏ธ ๐—š๐—˜๐—”๐—ฅ',
315
+ eggStock: '๐Ÿฅš ๐—˜๐—š๐—š๐—ฆ',
316
+ seedsStock: '๐ŸŒฑ ๐—ฆ๐—˜๐—˜๐——๐—ฆ',
317
+ easterStock: '๐Ÿฐ ๐—˜๐—”๐—ฆ๐—ง๐—˜๐—ฅ',
318
+ nightStock: '๐ŸŒ™ ๐—ก๐—œ๐—š๐—›๐—ง',
319
+ honeyStock: '๐Ÿฏ ๐—›๐—ข๐—ก๐—˜๐—ฌ',
320
+ cosmeticsStock: '๐Ÿ’„ ๐—–๐—ข๐—ฆ๐— ๐—˜๐—ง๐—œ๐—–๐—ฆ '
321
  };
322
 
323
  for (const [categoryKey, categoryName] of Object.entries(categories)) {
324
+ if (stockData[categoryKey] && stockData[categoryKey].length > 0) {
325
  message += `${categoryName}:\n`;
326
+ stockData[categoryKey].forEach(item => {
327
+ message += `โ€ข ${item.name}: ${item.value}\n`;
328
  });
329
+ message += '\n';
330
  }
331
  }
332
  return message;
333
  }
334
 
335
  function formatWeatherMessage(weatherData) {
336
+ if (!weatherData || !weatherData.status) return "โš ๏ธ Could not fetch weather data";
337
+
338
+ const weatherEmojis = {
339
+ rain: '๐ŸŒง๏ธ ๐—ฅ๐—”๐—œ๐—ก',
340
+ snow: 'โ„๏ธ ๐—ฆ๐—ก๐—ข๐—ช',
341
+ thunderstorm: 'โšก ๐—ง๐—›๐—จ๐—ก๐——๐—˜๐—ฅ๐—ฆ๐—ง๐—ข๐—ฅ๐— ',
342
+ bloodnight: '๐Ÿฉธ ๐—•๐—Ÿ๐—ข๐—ข๐——๐—ก๐—œ๐—š๐—›๐—ง',
343
+ meteorshower: 'โ˜„๏ธ ๐— ๐—˜๐—ง๐—˜๐—ข๐—ฅ ๐—ฆ๐—›๐—ข๐—ช๐—˜๐—ฅ',
344
+ disco: '๐Ÿชฉ ๐——๐—œ๐—ฆ๐—–๐—ข',
345
+ jandelstorm: '๐Ÿ•ฏ๏ธ ๐—๐—”๐—ก๐——๐—˜๐—Ÿ๐—ฆ๐—ง๐—ข๐—ฅ๐— ',
346
+ blackhole: '๐Ÿ•ณ๏ธ ๐—•๐—Ÿ๐—”๐—–๐—ž๐—›๐—ข๐—Ÿ๐—˜',
347
+ frost: 'โ„๏ธ ๐—™๐—ฅ๐—ข๐—ฆ๐—ง '
348
+ };
349
 
350
  let message = 'โ›… ๐—–๐—จ๐—ฅ๐—ฅ๐—˜๐—ก๐—ง ๐—ช๐—˜๐—”๐—ง๐—›๐—˜๐—ฅ ๐—–๐—ข๐—ก๐——๐—œ๐—ง๐—œ๐—ข๐—ก๐—ฆ\n\n';
351
+ const activeEvents = [];
352
 
353
+ for (const [event, data] of Object.entries(weatherData.response)) {
354
+ if (data.active) {
355
+ activeEvents.push(`${weatherEmojis[event] || '๐ŸŒ€'} ${event.toUpperCase()}`);
356
+ }
357
+ }
358
+
359
+ if (activeEvents.length === 0) {
360
+ message += "โ˜€๏ธ NO ACTIVE WEATHER EVENTS\n";
361
+ } else {
362
+ message += "๐ŸŸข ๐—”๐—–๐—ง๐—œ๐—ฉ๐—˜ ๐—ช๐—˜๐—”๐—ง๐—›๐—˜๐—ฅ ๐—˜๐—ฉ๐—˜๐—ก๐—ง๐—ฆ:\n";
363
+ message += activeEvents.join('\n');
364
+ }
365
 
366
  return message;
367
  }
 
396
  }
397
 
398
  const intervalId = setInterval(async () => {
399
+ const data = await getStockData();
400
+ if (data) {
401
+ await sendMessage(recipientId, formatStockMessage(data));
 
 
 
 
402
  }
403
  }, intervalMinutes * 60 * 1000);
404
 
 
540
  await sendMessage(senderId, helpMessage);
541
  }
542
  else if (command === '/stock') {
543
+ const data = await getStockData();
544
+ await sendMessage(senderId, data ? formatStockMessage(data) : 'โš ๏ธ Could not fetch stock data');
 
545
  }
546
  else if (command === '/weather') {
547
+ const weatherData = await getWeatherData();
 
548
  await sendMessage(senderId, weatherData ? formatWeatherMessage(weatherData) : 'โš ๏ธ Could not fetch weather data');
549
  }
550
  else if (command.startsWith('/gagstock')) {
 
559
  await sendMessage(senderId, 'โš ๏ธ Please provide valid minutes (e.g., /noti on 5)');
560
  return;
561
  }
 
562
  setupUserNotification(senderId, interval);
563
  await sendMessage(senderId, `๐Ÿ”” Notifications enabled! Updates every ${interval} minutes.`);
564
  }
 
576
  }
577
 
578
  loadUserPreferences();
 
579
  const PORT = process.env.PORT || 7860;
580
  app.listen(PORT, () => {
581
  console.log(`Bot running on port ${PORT}`);