Skip to content

Commit 6467e14

Browse files
committedOct 2, 2022
Feat: create a Flask App for webhook event subscriptions
1 parent 6c33409 commit 6467e14

12 files changed

+110
-49
lines changed
 

‎.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
TODOs.txt
2+
tokens.txt
23
__pycache__/
34
.idea/
45
.vscode/
5-
configfile/
6+
.env
67
logfile/

‎Procfile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: gunicorn --chdir ./src application:app

‎credentials/config.yaml

-7
This file was deleted.

‎credentials/tokens.txt

-3
This file was deleted.

‎requirements.txt

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
Flask
22
line-bot-sdk
3-
pyyaml
43
stravalib
54
requests
65
pandas
@@ -10,6 +9,6 @@ selenium
109
pyautogui
1110
pyperclip
1211
alive-progress
12+
python-dotenv
1313
gunicorn
14-
psycopg2
15-
14+
psycopg2

‎src/StravaUploadTool_Kernel.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ def upload_Fit_Activity_Files(access_token: str):
6262
with open(fitfile, 'rb') as fit_file:
6363
try:
6464
uploads_url = "https://www.strava.com/api/v3/uploads"
65-
payload = { 'client_id': STRAVA_CLIENT_ID,
66-
'data_type': 'fit' }
65+
payload = {'client_id': STRAVA_CLIENT_ID,
66+
'data_type': 'fit'}
6767
header = {'Authorization': 'Bearer ' + access_token}
6868
f = {'file': fit_file}
6969
r = requests.post(uploads_url,
@@ -81,11 +81,11 @@ def upload_Fit_Activity_Files(access_token: str):
8181
except (KeyError, TypeError, ValueError):
8282
return None
8383

84-
while (True):
84+
while True:
8585
# polling the upload status per semaild
8686
wait(1)
8787

88-
isError, activity_id = check_Upload_Status(access_token, fitfile, upload_ID)
88+
isError, activity_id = check_Upload_Status(access_token, upload_ID)
8989
time.sleep(0.05)
9090

9191
# If there is an error uploading activity file or
@@ -104,7 +104,7 @@ def upload_Fit_Activity_Files(access_token: str):
104104
print("")
105105

106106

107-
def check_Upload_Status(access_token: str, filename: str, upload_ID: str) -> Tuple[bool, Any]:
107+
def check_Upload_Status(access_token: str, upload_ID: str) -> Tuple[bool, Any]:
108108
try:
109109
uploads_url = "https://www.strava.com/api/v3/uploads/" + upload_ID
110110
header = {'Authorization': 'Bearer ' + access_token}

‎src/StravaUploadTool_Main.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ def main():
4343
""")
4444
preprocessing()
4545
fix_Fit_Activity_Files()
46-
TOKEN_FILE_PATH = Path('../credentials/tokens.txt')
47-
access_token = get_access_token(TOKEN_FILE_PATH)
46+
access_token = get_access_token()
4847
upload_Fit_Activity_Files(access_token)
4948

5049

‎src/application.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from stravalib import Client
2+
from authentication import *
3+
from sendLINEMessage import *
4+
from flask import Flask, request
5+
6+
7+
app = Flask(__name__)
8+
9+
10+
# Validate webhook subscriptions
11+
@app.get('/webhook')
12+
def webhook_get():
13+
data = request.args
14+
print(data)
15+
# Your verify token (should be a random string)
16+
VERIFY_TOKEN = "STRAVA"
17+
# Parse the query string parameters
18+
mode = data['hub.mode']
19+
verify_token = data['hub.verify_token']
20+
challenge = data['hub.challenge']
21+
if (mode != 'subscribe') or (verify_token != VERIFY_TOKEN):
22+
print('WEBHOOK_NOT_VERIFIED')
23+
return ('INVALID_REQUEST', 401)
24+
else:
25+
print('WEBHOOK_VERIFIED')
26+
return ({'hub.challenge': challenge}, 200)
27+
28+
29+
# Receive webhook events
30+
@app.post('/webhook')
31+
def webhook_post():
32+
print('EVENT_RECEIVED')
33+
data = request.json
34+
print(data)
35+
# You can do whatever you want upon receving a webhook event
36+
# Here we send a LINE message with the Strava activity link
37+
# when a non-indoor activity is created
38+
if (data["aspect_type"] == "create"):
39+
client = Client(access_token=get_access_token())
40+
activity = client.get_activity(data["object_id"])
41+
if (activity.trainer is True): # indoor activity
42+
print("Activity " + str(activity.id) + " is an indoor activity, please manually delete it via " +
43+
"https://www.strava.com/activities/" + str(activity.id))
44+
return ("INDOOR_ACTIVITY_CREATED", 200)
45+
else:
46+
if sendLINEMessage("https://www.strava.com/activities/" + str(activity.id)):
47+
print("LINE message sent!")
48+
return ("ACTIVITY_CREATED", 200)
49+
else:
50+
return ("SOMETHING_WAS_WRONG", 404)
51+
elif (data["aspect_type"] == "update"):
52+
return ("ACTIVITY_UPDATED", 200)
53+
elif (data["aspect_type"] == "delete"):
54+
return ("ACTIVITY_DELETED", 200)
55+
56+
57+
if __name__ == "__main__":
58+
app.run(debug=True)

‎src/authentication.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
client = Client()
1414

1515

16-
def get_access_token(file_path: pathlib.Path) -> str:
16+
def get_access_token(file_path: pathlib.Path = pathlib.Path('../tokens.txt')) -> str:
1717
"""
1818
Obtain and return an OAuth2 access token for the Strava V3 API
1919
@params:
@@ -50,14 +50,15 @@ def get_access_token(file_path: pathlib.Path) -> str:
5050
# ----------------
5151
def _read_tokens_from_file(file_path: pathlib.Path) -> dict:
5252
"""
53-
Read the authentication tokens and expiry time from a text file and return them as a dictionary
53+
Read the authentication tokens and expiry time from a text file
54+
and return them as a dictionary
5455
@params:
55-
file_path - The path of the file to read the tokens from.
56+
file_path - The path of the file to read the tokens from
5657
@return:
5758
A dictionary containing the authentication tokens and expiry time
5859
An empty dictionary if the file cannot be read from successfully
5960
"""
60-
print("[Strava]: Reading authentication tokens from '{}'...".format(file_path))
61+
print("[Strava]: Reading authentication tokens from '{}'".format(file_path))
6162

6263
tokens = {}
6364

@@ -85,8 +86,8 @@ def _write_tokens_to_file(file_path: pathlib.Path, tokens: dict):
8586
file_path - The path of the file to write the tokens to
8687
tokens - A dictionary containing the authentication tokens and expiry time to write to the file
8788
"""
88-
print("[Strava]: Writing authentication tokens to '{}'...".format(file_path))
89-
89+
print("[Strava]: Writing authentication tokens to '{}'".format(file_path))
90+
9091
# Delete the tokens file to remove any expired tokens
9192
try:
9293
file_path.unlink()

‎src/fitfiletools_automation.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def fix_Fit_Activity_Files():
162162
print("ERROR - Cannot find the element!")
163163
driver.quit()
164164

165-
# Step 12: finally, close the browser window and change back to the original working directory
165+
# Step 12: finally, close the web browser window and change back to the original working directory
166166
driver.quit()
167167
os.chdir(owd)
168168

‎src/read_config_file.py

+18-21
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,41 @@
1-
import yaml
2-
import sys
3-
from pathlib import Path
1+
import os
2+
from dotenv import load_dotenv
43

54

6-
CONFIG_FILE_PATH = Path('../credentials/config.yaml')
7-
with CONFIG_FILE_PATH.open(mode='r') as file:
8-
config = yaml.safe_load(file)
5+
load_dotenv("../.env") # take environment variables from .env
96

107

118
try:
12-
STRAVA_CLIENT_ID = config["STRAVA_CLIENT_ID"]
9+
STRAVA_CLIENT_ID = os.environ["STRAVA_CLIENT_ID"]
1310
except KeyError:
14-
sys.exit("[ERROR]: Please add STRAVA_CLIENT_ID to '{}'".format(CONFIG_FILE_PATH))
11+
print("[ERROR]: Please add STRAVA_CLIENT_ID to the list of environment variables!")
1512

1613
try:
17-
STRAVA_CLIENT_SECRET = config["STRAVA_CLIENT_SECRET"]
14+
STRAVA_CLIENT_SECRET = os.environ["STRAVA_CLIENT_SECRET"]
1815
except KeyError:
19-
sys.exit("[ERROR]: Please add STRAVA_CLIENT_SECRET to '{}'".format(CONFIG_FILE_PATH))
20-
16+
print("[ERROR]: Please add STRAVA_CLIENT_SECRET to the list of environment variables")
17+
2118
try:
22-
ZWIFT_ACTIVITY_DIR = config["ZWIFT_ACTIVITY_DIR"]
19+
GMAIL_USER_ID = os.environ["GMAIL_USER_ID"]
2320
except KeyError:
24-
sys.exit("[ERROR]: Please add ZWIFT_ACTIVITY_DIR to '{}'".format(CONFIG_FILE_PATH))
21+
print("[ERROR]: Please add GMAIL_USER_ID to the list of environment variables")
2522

2623
try:
27-
GMAIL_USER_ID = config["GMAIL_USER_ID"]
24+
GMAIL_PASSWORD = os.environ["GMAIL_PASSWORD"]
2825
except KeyError:
29-
sys.exit("[ERROR]: Please add GMAIL_USER_ID to '{}'".format(CONFIG_FILE_PATH))
26+
print("[ERROR]: Please add GMAIL_PASSWORD to the list of environment variables")
3027

3128
try:
32-
GMAIL_PASSWORD = config["GMAIL_PASSWORD"]
29+
LINE_CHANNEL_ACCESS_TOKEN = os.environ["LINE_CHANNEL_ACCESS_TOKEN"]
3330
except KeyError:
34-
sys.exit("[ERROR]: Please add GMAIL_PASSWORD to '{}'".format(CONFIG_FILE_PATH))
31+
print("[ERROR]: Please add LINE_CHANNEL_ACCESS_TOKEN to the list of environment variables")
3532

3633
try:
37-
LINE_CHANNEL_ACCESS_TOKEN = config["LINE_CHANNEL_ACCESS_TOKEN"]
34+
LINE_CHANNEL_SECRET = os.environ["LINE_CHANNEL_SECRET"]
3835
except KeyError:
39-
sys.exit("[ERROR]: Please add LINE_CHANNEL_ACCESS_TOKEN to '{}'".format(CONFIG_FILE_PATH))
36+
print("[ERROR]: Please add LINE_CHANNEL_SECRET to the list of environment variables")
4037

4138
try:
42-
LINE_CHANNEL_SECRET = config["LINE_CHANNEL_SECRET"]
39+
ZWIFT_ACTIVITY_DIR = os.environ["ZWIFT_ACTIVITY_DIR"]
4340
except KeyError:
44-
sys.exit("[ERROR]: Please add LINE_CHANNEL_SECRET to '{}'".format(CONFIG_FILE_PATH))
41+
print("[ERROR]: Please add ZWIFT_ACTIVITY_DIR to the list of environment variables")

‎src/sendLINEMessage.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from linebot import LineBotApi
2+
from linebot.models import TextSendMessage
3+
from linebot.exceptions import LineBotApiError
4+
from read_config_file import *
5+
6+
7+
line_bot_api = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
8+
9+
10+
def sendLINEMessage(StrvaActivityURL: str):
11+
try:
12+
line_bot_api.broadcast(TextSendMessage(text=StrvaActivityURL))
13+
except LineBotApiError as e:
14+
print("The was an error sending LINE message!")
15+
return 200

0 commit comments

Comments
 (0)
Please sign in to comment.