diff --git a/.gitignore b/.gitignore index 1da2241..3beed34 100644 --- a/.gitignore +++ b/.gitignore @@ -166,5 +166,3 @@ cython_debug/ *.secret *.db .DS_Store - -config.yml \ No newline at end of file diff --git a/config-sample.yml b/config-sample.yml deleted file mode 100644 index 44addea..0000000 --- a/config-sample.yml +++ /dev/null @@ -1,9 +0,0 @@ -stadiamaps: - api_key: -nominatim: - api_base_url: -mastodon: - client_id: - client_secret: - access_token: - api_base_url: diff --git a/requirements.txt b/requirements.txt index 02c2af3..cb414f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +beautifulsoup4==4.12.2 blurhash==1.1.4 certifi==2023.11.17 charset-normalizer==3.3.2 @@ -8,9 +9,9 @@ Mastodon.py==1.8.1 pillow==10.2.0 python-dateutil==2.8.2 python-magic==0.4.27 -PyYAML==6.0.1 requests==2.28.2 six==1.16.0 +soupsieve==2.5 SQLAlchemy==2.0.25 staticmap==0.5.7 typing_extensions==4.9.0 diff --git a/scl.py b/scl.py index 3514a0a..7bbc87a 100644 --- a/scl.py +++ b/scl.py @@ -1,10 +1,8 @@ import io -import math from datetime import datetime from typing import Optional import requests -import yaml from mastodon import Mastodon from PIL import Image, ImageDraw, ImageFont from sqlalchemy import create_engine, select @@ -22,15 +20,11 @@ except requests.JSONDecodeError: print("JSON could not be loaded from SCL API") raise -config = yaml.safe_load(open("config.yml")) -stadiamaps_api_key = config["stadiamaps"]["api_key"] -nominatim_url = config["nominatim"]["api_base_url"] -mastodon = Mastodon( - client_id=config["mastodon"]["client_id"], - client_secret=config["mastodon"]["client_secret"], - access_token=config["mastodon"]["access_token"], - api_base_url=config["mastodon"]["api_base_url"], -) +mastodon = Mastodon(access_token="scl_bot_mastodon.secret") + +with open("stadiamaps_api_key.secret", "r+") as stadiamaps_api_key_file: + # Reading from a file + stadiamaps_api_key = stadiamaps_api_key_file.read() class AttribStaticMap(StaticMap, object): @@ -187,7 +181,22 @@ with Session(engine) as session: # If the outage becomes medium/large, it'll then be posted as a new outage on the next run print("Outage is small, will not post") continue + print("Existing record not found") + post_text = """Seattle City Light is reporting a {} outage in {}. + +Start Date: {} +Est. Restoration: {} +Cause: {} + +{}""".format( + outage_size.lower(), + event["city"], + start_time.strftime(post_datetime_format), + estimated_restoration_time.strftime(post_datetime_format), + event["cause"], + hashtag_string, + ) try: map = AttribStaticMap( @@ -203,92 +212,10 @@ with Session(engine) as session: ) map.add_polygon(polygon) map_image = map.render() - - try: - - def num2deg(xtile, ytile, zoom): - n = 1 << zoom - lon_deg = xtile / n * 360.0 - 180.0 - lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) - lat_deg = math.degrees(lat_rad) - return lat_deg, lon_deg - - center_lat_lon = num2deg(map.x_center, map.y_center, map.zoom) - - # Zoom level 17 ensures that we won't get any building/POI names, just street names - geocode_url = "{nominatim_url}/reverse?lat={lat}&lon={lon}&format=geocodejson&zoom=17".format( - nominatim_url=nominatim_url, - lat=center_lat_lon[0], - lon=center_lat_lon[1], - ) - geocode_headers = {"User-Agent": "seattlecitylight-mastodon-bot"} - geocode_response = requests.get( - geocode_url, headers=geocode_headers - ) - try: - geocode = geocode_response.json() - except requests.JSONDecodeError: - print("JSON could not be loaded from nominatim API") - raise - - if ( - geocode["features"][0]["properties"]["geocoding"]["city"] - != "Seattle" - ): - city_not_seattle_text = " of {}".format( - geocode["features"][0]["properties"]["geocoding"]["city"] - ) - else: - city_not_seattle_text = "" - - street = geocode["features"][0]["properties"]["geocoding"]["name"] - - if "locality" in geocode["features"][0]["properties"]["geocoding"]: - locality = geocode["features"][0]["properties"]["geocoding"] - if locality == "Uptown": - locality = "Lower Queen Anne" - - alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format( - street, - locality, - city_not_seattle_text, - ) - area_text = "the {} area{}".format( - locality, city_not_seattle_text - ) - elif ( - "district" in geocode["features"][0]["properties"]["geocoding"] - ): - alt_text = "A map showing the location of the outage, centered around {} in the {} area{}.".format( - street, - geocode["features"][0]["properties"]["geocoding"][ - "district" - ], - city_not_seattle_text, - ) - area_text = "the {} area{}".format( - geocode["features"][0]["properties"]["geocoding"][ - "district" - ], - city_not_seattle_text, - ) - else: - alt_text = "A map showing the location of the outage, centered around {} in {}.".format( - street, - geocode["features"][0]["properties"]["geocoding"]["city"], - ) - area_text = geocode["features"][0]["properties"]["geocoding"][ - "city" - ] - except Exception: - alt_text = "A map showing the location of the outage." - with io.BytesIO() as map_image_file: map_image.save(map_image_file, format="PNG", optimize=True) map_media_post = mastodon.media_post( - map_image_file.getvalue(), - mime_type="image/png", - description=alt_text, + map_image_file.getvalue(), mime_type="image/png" ) except Exception as e: @@ -298,25 +225,6 @@ with Session(engine) as session: ) map_media_post = None - # Fallback location from the SCL API in case one couldn't be reverse geocoded - if not area_text: - area_text = event["city"] - - post_text = """Seattle City Light is reporting a {} outage in {}. - -Start Date: {} -Est. Restoration: {} -Cause: {} - -{}""".format( - outage_size.lower(), - area_text, - start_time.strftime(post_datetime_format), - estimated_restoration_time.strftime(post_datetime_format), - event["cause"], - hashtag_string, - ) - mastodon_post_result = mastodon.status_post( status=post_text, media_ids=map_media_post,