Skip to content

Commit

Permalink
Merge pull request #4 from caarmen/modularize-code
Browse files Browse the repository at this point in the history
Modularize code
  • Loading branch information
caarmen authored May 19, 2023
2 parents 0115046 + a1e1e74 commit 169e694
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 208 deletions.
8 changes: 4 additions & 4 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Required parameters:
WEATHERSTACK_API_ACCESS_KEY=0123456789abcdef0123456789abcdef
SLACK_WORKSPACE=myworkspace.slack.com
SLACK_TOKEN=xoxc-01234567890-012345678912-0123456789123-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
SLACK_COOKIE_D=xoxd-00000000000000000000000%2B0000000000000000000000000%2B000000000000000000000000000000000000000000000000%2F0000000000%2B0000000000000000000000000000000000%2F000%2F0000000000000000000000000000000000%3D
WEATHERSTACK__API_ACCESS_KEY=0123456789abcdef0123456789abcdef
SLACK__WORKSPACE=myworkspace.slack.com
SLACK__TOKEN=xoxc-01234567890-012345678912-0123456789123-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
SLACK__COOKIE_D=xoxd-00000000000000000000000%2B0000000000000000000000000%2B000000000000000000000000000000000000000000000000%2F0000000000%2B0000000000000000000000000000000000%2F000%2F0000000000000000000000000000000000%3D
LATITUDE=34.056389
LONGITUDE=-117.821667

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.env*
.resources
.vscode/
__pycache__
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ COPY requirements.txt requirements.txt

RUN pip install -r requirements.txt

COPY main.py main.py
COPY wspp wspp
COPY resources resources

CMD python main.py
CMD python wspp.main.py
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ You need to provide:
* Environment variables:
- Copy the `.env.template` file to `.env`.
- Edit the variables:
- `WEATHERSTACK_API_ACCESS_KEY`: the api key for your account on weatherstack.com.
- `SLACK_WORKSPACE`: the slack workspace where you want to change your profile photo
- `SLACK_TOKEN`, `SLACK_COOKIE_D`: see below.
- `WEATHERSTACK__API_ACCESS_KEY`: the api key for your account on weatherstack.com.
- `SLACK__WORKSPACE`: the slack workspace where you want to change your profile photo
- `SLACK__TOKEN`, `SLACK__COOKIE_D`: see below.
- `LATITUDE`, `LONGITUDE`: the location for the weather conditions that will be used to fill in the background in your profile photo.

### Retrieving the slack token and cookie values
* Log into slack in a browser on a computer.
* Enable developer tools in the browser, and open the network tab.
* Manually change your profile photo to any photo you want.
* Inspect the request in the developer tools network tab on the `/api/users.setPhoto` endpoint.
- Copy the value of the `token` parameter into `SLACK_TOKEN` in your `.env` file.
- For the `SLACK_COOKIE_D` env var, look at the `cookie` header, and extract the value of the `d=` part of the cookie.
- Copy the value of the `token` parameter into `SLACK__TOKEN` in your `.env` file.
- For the `SLACK__COOKIE_D` env var, look at the `cookie` header, and extract the value of the `d=` part of the cookie.

### Optional parameters
* `PROFILE_PHOTOS_DIR`: the directory containing your profile photos with the transparent background. It should contain a `photo.png` for daytime, and a `night_photo.png` for nighttime. The default location is in `profile_photos/` in the project.
Expand All @@ -31,7 +31,7 @@ You need to provide:

### Locally
* Setup a python environment
* Run `python main.py`
* Run `python wspp.main.py`

### With Docker

Expand All @@ -50,4 +50,4 @@ docker run --detach -v `pwd`/.env:/app/.env -v `pwd`/profile_photos/:/app/profil
## Image credits
* The night background photo is from [Alexey Elfimov](https://commons.wikimedia.org/wiki/File:%D0%A1%D0%B2%D0%B5%D1%82_%D0%BE%D1%82_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BD%D0%B8_-_panoramio.jpg), licensed under the Creative Commons Attribution 3.0 Unported license
* The weather background images were generated by Bing
* The sample profile photos were generated from the Bitmoji app.
* The sample profile photos were generated from the Bitmoji app.
194 changes: 0 additions & 194 deletions main.py

This file was deleted.

2 changes: 1 addition & 1 deletion scripts/codecheck.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
for project in .
for project in wspp
do
black $project
ruff check $project
Expand Down
Empty file added wspp/__init__.py
Empty file.
32 changes: 32 additions & 0 deletions wspp/image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import io
from pathlib import Path

from PIL import Image

_resources_dir = Path(__file__).parent.parent / "resources"


def create_profile_photo(background: Path, foreground: Path) -> io.BytesIO:
"""
:return: the binary data of an image containing the given background image, with
the provided foreground image on top.
"""
background_image = Image.open(background)
foreground_image = Image.open(foreground).convert("RGBA")
new_image = Image.new(
mode="RGB",
size=background_image.size,
)
new_image.paste(background_image)
new_image.paste(foreground_image, (0, 0), foreground_image)
bio = io.BytesIO()
new_image.save(bio, format="JPEG")
bio.seek(0)
return bio


def get_image_file(prefix: str) -> Path:
"""
:return: the background image file starting with the given prefix
"""
return next(_resources_dir.glob(f"{prefix}*"))
97 changes: 97 additions & 0 deletions wspp/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Script which periodically:
* fetches the weather condition of a specified location,
* selects a background image based on the weather condition,
* creates a new profile photo using this background image and a provided profile photo,
* and updates the user's profile photo in Slack with this new profile photo.
"""

import dataclasses
import logging
from pathlib import Path
from threading import Event
from typing import Optional

from pydantic import BaseSettings, DirectoryPath, PositiveInt

from wspp import image, schedule, slack, weatherstack
from wspp.sunrisesunset import SunriseSunset


class Settings(BaseSettings):
weatherstack: weatherstack.WeatherstackSettings
slack: slack.SlackSettings
latitude: float
longitude: float
profile_photos_dir: DirectoryPath = Path(__file__).parent.parent / "profile_photos"
polling_interval_s: PositiveInt = 7200

class Config:
env_file = ".env"
env_nested_delimiter = "__"


settings = Settings()


@dataclasses.dataclass
class Cache:
last_weather_code: Optional[int] = None


cache = Cache()


def setup_logging():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)


def update_profile_photo_from_weather():
"""
Update the profile photo on slack based on the current weather condition.
"""
try:
sunrise_sunset = SunriseSunset.create(
latitude=settings.latitude, longitude=settings.longitude
)
weather_code = weatherstack.get_current_weather_code(
settings=settings.weatherstack,
latitude=settings.latitude,
longitude=settings.longitude,
is_day=sunrise_sunset.is_day_now,
)
if cache.last_weather_code != weather_code:
cache.last_weather_code = weather_code

background_image_file = image.get_image_file(prefix=str(weather_code))
foreground_image_file = Path(settings.profile_photos_dir) / (
"photo.png" if sunrise_sunset.is_day_now else "night_photo.png"
)
profile_photo = image.create_profile_photo(
background=background_image_file,
foreground=foreground_image_file,
)
slack.set_profile_photo(settings.slack, profile_photo)
logging.info(
f"updated profile photo based on weather_code {weather_code}"
+ f" and background image {background_image_file}"
)
else:
logging.info(f"No weather change since last time ({weather_code})")
except Exception:
logging.error("Error updating profile photo", exc_info=True)

schedule.schedule(
daytime_interval_s=settings.polling_interval_s,
sunrise_sunset=sunrise_sunset,
function=update_profile_photo_from_weather,
)


if __name__ == "__main__":
setup_logging()
update_profile_photo_from_weather()
Event().wait()
Loading

0 comments on commit 169e694

Please sign in to comment.