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
|
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
DuckDuckGo Weather
~~~~~~~~~~~~~~~~~~
"""
from typing import TYPE_CHECKING
from json import loads
from urllib.parse import quote
from dateutil import parser as date_parser
from searx.engines.duckduckgo import fetch_traits # pylint: disable=unused-import
from searx.engines.duckduckgo import get_ddg_lang
from searx.enginelib.traits import EngineTraits
from searx.result_types import EngineResults, WeatherAnswer
from searx import weather
if TYPE_CHECKING:
import logging
logger: logging.Logger
traits: EngineTraits
about = {
"website": 'https://duckduckgo.com/',
"wikidata_id": 'Q12805',
"official_api_documentation": None,
"use_official_api": True,
"require_api_key": False,
"results": "JSON",
}
send_accept_language_header = True
# engine dependent config
categories = ["weather"]
base_url = "https://duckduckgo.com/js/spice/forecast/{query}/{lang}"
# adapted from https://gist.github.com/mikesprague/048a93b832e2862050356ca233ef4dc1
WEATHERKIT_TO_CONDITION = {
"BlowingDust": "fog",
"Clear": "clear",
"Cloudy": "cloudy",
"Foggy": "fog",
"Haze": "fog",
"MostlyClear": "clear",
"MostlyCloudy": "partly cloudy",
"PartlyCloudy": "partly cloudy",
"Smoky": "fog",
"Breezy": "partly cloudy",
"Windy": "partly cloudy",
"Drizzle": "light rain",
"HeavyRain": "heavy rain",
"IsolatedThunderstorms": "rain and thunder",
"Rain": "rain",
"SunShowers": "rain",
"ScatteredThunderstorms": "heavy rain and thunder",
"StrongStorms": "heavy rain and thunder",
"Thunderstorms": "rain and thunder",
"Frigid": "clear sky",
"Hail": "heavy rain",
"Hot": "clear sky",
"Flurries": "light snow",
"Sleet": "sleet",
"Snow": "light snow",
"SunFlurries": "light snow",
"WintryMix": "sleet",
"Blizzard": "heavy snow",
"BlowingSnow": "heavy snow",
"FreezingDrizzle": "light sleet",
"FreezingRain": "sleet",
"HeavySnow": "heavy snow",
"Hurricane": "rain and thunder",
"TropicalStorm": "rain and thunder",
}
def _weather_data(location, data):
return WeatherAnswer.Item(
location=location,
temperature=weather.Temperature(unit="°C", value=data['temperature']),
condition=WEATHERKIT_TO_CONDITION[data["conditionCode"]],
feels_like=weather.Temperature(unit="°C", value=data['temperatureApparent']),
wind_from=weather.Compass(data["windDirection"]),
wind_speed=weather.WindSpeed(data["windSpeed"], unit="mi/h"),
pressure=weather.Pressure(data["pressure"], unit="hPa"),
humidity=weather.RelativeHumidity(data["humidity"] * 100),
cloud_cover=data["cloudCover"] * 100,
)
def request(query, params):
eng_region = traits.get_region(params['searxng_locale'], traits.all_locale)
eng_lang = get_ddg_lang(traits, params['searxng_locale'])
# !ddw paris :es-AR --> {'ad': 'es_AR', 'ah': 'ar-es', 'l': 'ar-es'}
params['cookies']['ad'] = eng_lang
params['cookies']['ah'] = eng_region
params['cookies']['l'] = eng_region
logger.debug("cookies: %s", params['cookies'])
params["url"] = base_url.format(query=quote(query), lang=eng_lang.split('_')[0])
return params
def response(resp):
res = EngineResults()
if resp.text.strip() == "ddg_spice_forecast();":
return res
json_data = loads(resp.text[resp.text.find('\n') + 1 : resp.text.rfind('\n') - 2])
geoloc = weather.GeoLocation.by_query(resp.search_params["query"])
weather_answer = WeatherAnswer(
current=_weather_data(geoloc, json_data["currentWeather"]),
service="duckduckgo weather",
)
for forecast in json_data['forecastHourly']['hours']:
forecast_time = date_parser.parse(forecast['forecastStart'])
forecast_data = _weather_data(geoloc, forecast)
forecast_data.datetime = weather.DateTime(forecast_time)
weather_answer.forecasts.append(forecast_data)
res.add(weather_answer)
return res
|