From ea241540b97dfd9f8db37d3c905664e9d47ee9cc Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Tue, 29 Apr 2025 13:43:53 -0400 Subject: [PATCH] dev: add WeatherService - adds weather service - updates IdentificationService to add ip_user property --- .../external-extras/ExternalExtrasModule.js | 4 + .../modules/external-extras/WeatherService.js | 92 +++++++++++++++++++ .../abuse-prevention/IdentificationService.js | 2 + 3 files changed, 98 insertions(+) create mode 100644 src/backend/src/modules/external-extras/WeatherService.js diff --git a/src/backend/src/modules/external-extras/ExternalExtrasModule.js b/src/backend/src/modules/external-extras/ExternalExtrasModule.js index a501a622a..69547c99a 100644 --- a/src/backend/src/modules/external-extras/ExternalExtrasModule.js +++ b/src/backend/src/modules/external-extras/ExternalExtrasModule.js @@ -13,6 +13,10 @@ class ExternalExtrasModule extends AdvancedBase { const { NewsDataService } = require('./NewsDataService'); services.registerService('newsdata', NewsDataService); } + if ( !! config?.services?.weather ) { + const { WeatherService } = require('./WeatherService'); + services.registerService('weather', WeatherService); + } } } diff --git a/src/backend/src/modules/external-extras/WeatherService.js b/src/backend/src/modules/external-extras/WeatherService.js new file mode 100644 index 000000000..cd789a0b9 --- /dev/null +++ b/src/backend/src/modules/external-extras/WeatherService.js @@ -0,0 +1,92 @@ +const BaseService = require('../../services/BaseService'); +const { Context } = require('../../util/context'); + +class WeatherService extends BaseService { + async ['__on_driver.register.interfaces'] () { + const svc_registry = this.services.get('registry'); + const col_interfaces = svc_registry.get('interfaces'); + + const common = { + default_parameter: 'q', + parameters: { + '*': { + type: 'json', + }, + }, + result: { + type: 'json' + }, + }; + + col_interfaces.set('weather', { + description: 'weatherapi.com', + methods: { + weather: { + description: 'Report current weather in the specified location', + ...common, + }, + forecast: { + description: 'Report the weather forecast for the specified location', + ...common, + }, + } + }); + } + async _init () { + this.baseURL = 'https://api.weatherapi.com/v1'; + } + static IMPLEMENTS = { + weather: { + // We call this "weather" instead of "current" so that it can be + // the default method for the weather driver. + async weather (parameters) { + return await this.general('current.json', parameters); + }, + async forecast (parameters) { + // Okay this is kinda dumb but the default behavior for forecast + // is that it forecasts 1 day - the current day. I'm going to make + // the default 5 days here because I think that's what most people + // will expect. + + if ( parameters.days === undefined ) { + parameters.days = 5; + } + + return await this.general('forecast.json', parameters); + } + } + } + + async general (component, parameters) { + const require = this.require; + + const axios = require('axios'); + const querystring = require('querystring'); + + if ( ! parameters.q ) { + const requester = Context.get('requester'); + parameters.q = requester.ip_user ?? + '-77.6776746,165.2019492'; // McMurdo Station, Antarctica + } + + const qstr = querystring.stringify({ + ...parameters, + key: this.config.apiKey, + }); + + const req_options = { + method: 'GET', + url: this.baseURL + `/${component}?` + qstr, + }; + + console.log('debug the request', req_options); + + const resp = await axios.request(req_options); + + return resp.data; + } +} + +module.exports = { + WeatherService, +}; diff --git a/src/backend/src/services/abuse-prevention/IdentificationService.js b/src/backend/src/services/abuse-prevention/IdentificationService.js index 2a1715285..4d5ce0001 100644 --- a/src/backend/src/services/abuse-prevention/IdentificationService.js +++ b/src/backend/src/services/abuse-prevention/IdentificationService.js @@ -57,6 +57,8 @@ class Requester { ua: req.headers['user-agent'], ip: req.connection.remoteAddress, ip_forwarded: req.headers['x-forwarded-for'], + ip_user: req.headers['x-forwarded-for'] || + req.connection.remoteAddress, origin: req.headers['origin'], referer: req.headers['referer'], referer_origin,