forked from O365/python-o365
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsubscriptions_example.py
More file actions
132 lines (107 loc) · 4.88 KB
/
Copy pathsubscriptions_example.py
File metadata and controls
132 lines (107 loc) · 4.88 KB
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
""" Example on how to use and setup webhooks
Quickstart for this example:
1) Run Flask locally withg the following command:
- flask --app examples/subscriptions_example.py run --debug
2) Expose HTTPS via a tunnel to your localhost:5000:
- Free: pinggy (https://pinggy.io/) to get https://<subdomain>.pinggy.link -> http://localhost:5000
- Paid/free-tier: ngrok (https://ngrok.com/): ngrok http 5000, note the https URL.
3) Use the tunnel HTTPS URL as notification_url pointing to /webhook, URL-encoded.
4) To create a subscription, follow the example request below:
- https://<your-tunnel-host>/subscriptions?notification_url=https%3A%2F%2F<your-tunnel-host>%2Fwebhook&client_state=abc123
5) To list subscriptions, follow the example request below:
- http://<your-tunnel-host>/subscriptions/list
6) To renew a subscription, follow the example request below:
- http://<your-tunnel-host>/subscriptions/<subscription_id>/renew?expiration_minutes=55
7) To delete a subscription, follow the example request below:
- http://<your-tunnel-host>/subscriptions/<subscription_id>/delete
Graph will call https://<your-tunnel-host>/webhook; this app echoes validationToken and returns 202 for notifications.
"""
from flask import Flask, abort, jsonify, request
from O365 import Account
CLIENT_ID = "YOUR CLIENT ID"
CLIENT_SECRET = "YOUR CLIENT SECRET"
credentials = (CLIENT_ID, CLIENT_SECRET)
account = Account(credentials)
# Pick the scopes that are relevant to you here
account.authenticate(
scopes=[
"https://graph.microsoft.com/Mail.ReadWrite",
"https://graph.microsoft.com/Mail.Send",
"https://graph.microsoft.com/Calendars.ReadWrite",
"https://graph.microsoft.com/MailboxSettings.ReadWrite",
"https://graph.microsoft.com/User.Read",
"https://graph.microsoft.com/User.ReadBasic.All",
'offline_access'
])
RESOURCE = "/me/mailFolders('inbox')/messages"
DEFAULT_EXPIRATION_MINUTES = 10069 # Maximum expiration is 10,070 in the future.
app = Flask(__name__)
def _int_arg(name: str, default: int) -> int:
raw = request.args.get(name)
if raw is None:
return default
try:
return int(raw)
except ValueError:
abort(400, description=f"{name} must be an integer")
@app.get("/subscriptions")
def create_subscription():
notification_url = request.args.get("notification_url")
if not notification_url:
abort(400, description="notification_url is required")
expiration_minutes = _int_arg("expiration_minutes", DEFAULT_EXPIRATION_MINUTES)
client_state = request.args.get("client_state")
resource = request.args.get("resource", RESOURCE)
subscription = account.subscriptions().create_subscription(
notification_url=notification_url,
resource=resource,
change_type="created",
expiration_minutes=expiration_minutes,
client_state=client_state,
)
return jsonify(subscription), 201
@app.get("/subscriptions/list")
def list_subscriptions():
limit_raw = request.args.get("limit")
limit = None
if limit_raw is not None:
try:
limit = int(limit_raw)
except ValueError:
abort(400, description="limit must be an integer")
if limit <= 0:
abort(400, description="limit must be a positive integer")
subscriptions = account.subscriptions().list_subscriptions(limit=limit)
return jsonify(list(subscriptions)), 200
@app.get("/subscriptions/<subscription_id>/renew")
def renew_subscription(subscription_id: str):
expiration_minutes = _int_arg("expiration_minutes", DEFAULT_EXPIRATION_MINUTES)
updated = account.subscriptions().renew_subscription(
subscription_id,
expiration_minutes=expiration_minutes,
)
return jsonify(updated), 200
@app.get("/subscriptions/<subscription_id>/delete")
def delete_subscription(subscription_id: str):
deleted = account.subscriptions().delete_subscription(subscription_id)
if not deleted:
abort(404, description="Subscription not found")
return ("", 204)
@app.post("/webhook")
def webhook_handler():
"""Handle Microsoft Graph webhook calls.
- During subscription validation, Graph sends POST with ?validationToken=... .
We must echo the token as plain text within 10 seconds.
- For change notifications, Graph posts JSON; we just log/ack.
"""
validation_token = request.args.get("validationToken")
if validation_token:
# Echo back token exactly as plain text with HTTP 200.
return validation_token, 200, {"Content-Type": "text/plain"}
# Change notifications: inspect or log as needed.
payload = request.get_json(silent=True) or {}
print("Received notification payload:", payload)
return ("", 202)
if __name__ == "__main__":
app.run(debug=True, ssl_context=("examples/cert.pem", "examples/key.pem"))