Add better time zone city querying using OpenStreetMap
This commit is contained in:
parent
06dadf14fe
commit
66665462a4
@ -1,7 +1,9 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import aiohttp
|
||||
import dateparser
|
||||
import humanize
|
||||
import timezonefinder
|
||||
import pytz
|
||||
|
||||
import pluralkit.bot.embeds
|
||||
@ -9,6 +11,9 @@ from pluralkit.bot.commands import *
|
||||
from pluralkit.errors import ExistingSystemError, UnlinkingLastAccountError, AccountAlreadyLinkedError
|
||||
from pluralkit.utils import display_relative
|
||||
|
||||
# This needs to load from the timezone file so we're preloading this so we
|
||||
# don't have to do it on every invocation
|
||||
tzf = timezonefinder.TimezoneFinder()
|
||||
|
||||
async def system_root(ctx: CommandContext):
|
||||
# Commands that operate without a specified system (usually defaults to the executor's own system)
|
||||
@ -100,12 +105,34 @@ async def system_description(ctx: CommandContext):
|
||||
|
||||
async def system_timezone(ctx: CommandContext):
|
||||
system = await ctx.ensure_system()
|
||||
new_tz = ctx.remaining() or None
|
||||
city_query = ctx.remaining() or None
|
||||
|
||||
tz = await system.set_time_zone(ctx.conn, new_tz)
|
||||
msg = await ctx.reply("\U0001F50D Searching '{}' (may take a while)...".format(city_query))
|
||||
|
||||
# Look up the city on Overpass (OpenStreetMap)
|
||||
async with aiohttp.ClientSession() as sess:
|
||||
# OverpassQL is weird, but this basically searches for every node of type city with name [input].
|
||||
async with sess.get("https://nominatim.openstreetmap.org/search?city=novosibirsk&format=json&limit=1", params={"city": city_query, "format": "json", "limit": "1"}) as r:
|
||||
if r.status != 200:
|
||||
raise CommandError("OSM Nominatim API returned error. Try again.")
|
||||
data = await r.json()
|
||||
|
||||
# If we didn't find a city, complain
|
||||
if not data:
|
||||
raise CommandError("City '{}' not found.".format(city_query))
|
||||
|
||||
# Take the lat/long given by Overpass and put it into timezonefinder
|
||||
lat, lng = (float(data[0]["lat"]), float(data[0]["lon"]))
|
||||
timezone_name = tzf.timezone_at(lng=lng, lat=lat)
|
||||
if not timezone_name:
|
||||
raise CommandError("Time zone for city '{}' not found. This should never happen.".format(data[0]["display_name"]))
|
||||
|
||||
# This should hopefully result in a valid time zone name
|
||||
# (if not, something went wrong)
|
||||
tz = await system.set_time_zone(ctx.conn, timezone_name)
|
||||
offset = tz.utcoffset(datetime.utcnow())
|
||||
offset_str = "UTC{:+02d}:{:02d}".format(int(offset.total_seconds() // 3600), int(offset.total_seconds() // 60 % 60))
|
||||
await ctx.reply_ok("System time zone set to {} ({}, {}).".format(tz.tzname(datetime.utcnow()), offset_str, tz.zone))
|
||||
await ctx.reply_ok("System time zone set to {} ({}, {}).\n*Data from OpenStreetMap, queried using Nominatim.*".format(tz.tzname(datetime.utcnow()), offset_str, tz.zone))
|
||||
|
||||
|
||||
async def system_tag(ctx: CommandContext):
|
||||
|
@ -12,20 +12,6 @@ from pluralkit.member import Member
|
||||
from pluralkit.switch import Switch
|
||||
from pluralkit.utils import generate_hid, contains_custom_emoji, validate_avatar_url_or_raise
|
||||
|
||||
|
||||
def canonicalize_tz_name(name: str) -> Optional[str]:
|
||||
# First, try a direct search
|
||||
try:
|
||||
pytz.timezone(name)
|
||||
return name
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pass
|
||||
|
||||
# Then check last fragment of common time zone identifiers
|
||||
name_map = {tz.split("/")[-1].replace("_", " "): tz for tz in pytz.common_timezones}
|
||||
if name in name_map:
|
||||
return name_map[name]
|
||||
|
||||
class TupperboxImportResult(namedtuple("TupperboxImportResult", ["updated", "created", "tags"])):
|
||||
pass
|
||||
|
||||
@ -248,11 +234,7 @@ class System(namedtuple("System", ["id", "hid", "name", "description", "tag", "a
|
||||
:returns: The `pytz.tzinfo` instance of the newly set time zone.
|
||||
"""
|
||||
|
||||
canonical_name = canonicalize_tz_name(tz_name or "UTC")
|
||||
if not canonical_name:
|
||||
raise errors.InvalidTimeZoneError(tz_name)
|
||||
tz = pytz.timezone(canonical_name)
|
||||
|
||||
tz = pytz.timezone(tz_name or "UTC")
|
||||
await db.update_system_field(conn, self.id, "ui_tz", tz.zone)
|
||||
return tz
|
||||
|
||||
|
@ -6,4 +6,5 @@ https://github.com/Rapptz/discord.py/archive/860d6a9ace8248dfeec18b8b159e7b757d9
|
||||
humanize
|
||||
uvloop; sys.platform != 'win32' and sys.platform != 'cygwin' and sys.platform != 'cli'
|
||||
ciso8601
|
||||
pytz
|
||||
pytz
|
||||
timezonefinder
|
Loading…
Reference in New Issue
Block a user