webex-email-reminders 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- webex_email_reminders-0.1.0/PKG-INFO +7 -0
- webex_email_reminders-0.1.0/README.md +56 -0
- webex_email_reminders-0.1.0/pyproject.toml +16 -0
- webex_email_reminders-0.1.0/setup.cfg +4 -0
- webex_email_reminders-0.1.0/webex_email_reminders.egg-info/PKG-INFO +7 -0
- webex_email_reminders-0.1.0/webex_email_reminders.egg-info/SOURCES.txt +9 -0
- webex_email_reminders-0.1.0/webex_email_reminders.egg-info/dependency_links.txt +1 -0
- webex_email_reminders-0.1.0/webex_email_reminders.egg-info/entry_points.txt +2 -0
- webex_email_reminders-0.1.0/webex_email_reminders.egg-info/requires.txt +2 -0
- webex_email_reminders-0.1.0/webex_email_reminders.egg-info/top_level.txt +1 -0
- webex_email_reminders-0.1.0/webex_reminders.py +192 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Webex Email Reminders
|
|
2
|
+
|
|
3
|
+
CLI tool that checks Webex for mentions and direct messages from the last hour and sends an email summary via Nylas.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Python 3.9+
|
|
8
|
+
- Nylas CLI: `brew install nylas/nylas-cli/nylas`
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install -r requirements.txt
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Environment Variables
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
export WEBEX_JWT="your-webex-personal-access-token"
|
|
20
|
+
export NYLAS_API_KEY="your-nylas-api-key"
|
|
21
|
+
export NYLAS_GRANT_ID="your-nylas-grant-id"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Send summary email
|
|
28
|
+
python webex_reminders.py --to your@email.com
|
|
29
|
+
|
|
30
|
+
# Custom time window
|
|
31
|
+
python webex_reminders.py --to your@email.com --hours 2
|
|
32
|
+
|
|
33
|
+
# Filter DMs by contacts file
|
|
34
|
+
python webex_reminders.py --to your@email.com --contacts contacts.txt
|
|
35
|
+
|
|
36
|
+
# Dry run (prints email content without sending)
|
|
37
|
+
python webex_reminders.py --to your@email.com --dry-run
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Options
|
|
41
|
+
|
|
42
|
+
| Flag | Description |
|
|
43
|
+
|------|-------------|
|
|
44
|
+
| `--to` | Email address to send summary to (required) |
|
|
45
|
+
| `--hours` | Look back period in hours (default: 1) |
|
|
46
|
+
| `--contacts` | Text file with email addresses to filter DMs |
|
|
47
|
+
| `--dry-run` | Print email content to stdout without sending |
|
|
48
|
+
|
|
49
|
+
## Contacts File (optional)
|
|
50
|
+
|
|
51
|
+
One email per line to filter which DMs to include:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
person1@company.com
|
|
55
|
+
person2@company.com
|
|
56
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "webex-email-reminders"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "CLI tool that monitors Webex mentions and DMs, sends email summaries via Nylas"
|
|
9
|
+
requires-python = ">=3.9"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"nylas==6.2.0",
|
|
12
|
+
"requests==2.32.3",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
webex-reminders = "webex_reminders:main"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
webex_reminders.py
|
|
4
|
+
webex_email_reminders.egg-info/PKG-INFO
|
|
5
|
+
webex_email_reminders.egg-info/SOURCES.txt
|
|
6
|
+
webex_email_reminders.egg-info/dependency_links.txt
|
|
7
|
+
webex_email_reminders.egg-info/entry_points.txt
|
|
8
|
+
webex_email_reminders.egg-info/requires.txt
|
|
9
|
+
webex_email_reminders.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
webex_reminders
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Webex Email Reminders - Check Webex mentions and DMs, send email summary via Nylas."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from datetime import datetime, timedelta, timezone
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from nylas import Client as NylasClient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_webex_headers():
|
|
14
|
+
token = os.environ.get("WEBEX_JWT")
|
|
15
|
+
if not token:
|
|
16
|
+
sys.exit("Error: WEBEX_JWT environment variable not set")
|
|
17
|
+
return {"Authorization": f"Bearer {token}"}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_me(headers):
|
|
21
|
+
resp = requests.get("https://webexapis.com/v1/people/me", headers=headers)
|
|
22
|
+
resp.raise_for_status()
|
|
23
|
+
return resp.json()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_mentions(headers, since):
|
|
27
|
+
"""Get messages where you are mentioned across all spaces."""
|
|
28
|
+
rooms_resp = requests.get(
|
|
29
|
+
"https://webexapis.com/v1/rooms", headers=headers, params={"max": 50, "sortBy": "lastactivity"}
|
|
30
|
+
)
|
|
31
|
+
rooms_resp.raise_for_status()
|
|
32
|
+
|
|
33
|
+
messages = []
|
|
34
|
+
for room in rooms_resp.json().get("items", []):
|
|
35
|
+
last_activity = datetime.fromisoformat(room["lastActivity"].replace("Z", "+00:00"))
|
|
36
|
+
if last_activity < since:
|
|
37
|
+
break
|
|
38
|
+
if room.get("type") == "direct":
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
msgs_resp = requests.get(
|
|
42
|
+
"https://webexapis.com/v1/messages",
|
|
43
|
+
headers=headers,
|
|
44
|
+
params={"roomId": room["id"], "mentionedPeople": "me", "max": 20},
|
|
45
|
+
)
|
|
46
|
+
if msgs_resp.status_code != 200:
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
for msg in msgs_resp.json().get("items", []):
|
|
50
|
+
created = datetime.fromisoformat(msg["created"].replace("Z", "+00:00"))
|
|
51
|
+
if created >= since:
|
|
52
|
+
msg["_spaceName"] = room.get("title", "Unknown Space")
|
|
53
|
+
messages.append(msg)
|
|
54
|
+
return messages
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_direct_messages(headers, since, contacts_file=None):
|
|
58
|
+
"""Get direct messages from the last hour, optionally filtered by a contacts file."""
|
|
59
|
+
allowed_emails = None
|
|
60
|
+
if contacts_file and os.path.exists(contacts_file):
|
|
61
|
+
with open(contacts_file) as f:
|
|
62
|
+
allowed_emails = {line.strip().lower() for line in f if line.strip()}
|
|
63
|
+
|
|
64
|
+
rooms_resp = requests.get(
|
|
65
|
+
"https://webexapis.com/v1/rooms", headers=headers, params={"type": "direct", "max": 50}
|
|
66
|
+
)
|
|
67
|
+
rooms_resp.raise_for_status()
|
|
68
|
+
|
|
69
|
+
messages = []
|
|
70
|
+
for room in rooms_resp.json().get("items", []):
|
|
71
|
+
last_activity = datetime.fromisoformat(room["lastActivity"].replace("Z", "+00:00"))
|
|
72
|
+
if last_activity < since:
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
msgs_resp = requests.get(
|
|
76
|
+
"https://webexapis.com/v1/messages",
|
|
77
|
+
headers=headers,
|
|
78
|
+
params={"roomId": room["id"], "max": 20},
|
|
79
|
+
)
|
|
80
|
+
msgs_resp.raise_for_status()
|
|
81
|
+
|
|
82
|
+
for msg in msgs_resp.json().get("items", []):
|
|
83
|
+
created = datetime.fromisoformat(msg["created"].replace("Z", "+00:00"))
|
|
84
|
+
if created < since:
|
|
85
|
+
break
|
|
86
|
+
sender = msg.get("personEmail", "").lower()
|
|
87
|
+
if allowed_emails and sender not in allowed_emails:
|
|
88
|
+
continue
|
|
89
|
+
messages.append(msg)
|
|
90
|
+
|
|
91
|
+
return messages
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def format_email_body(mentions, dms):
|
|
95
|
+
"""Format mentions and DMs into an HTML email body."""
|
|
96
|
+
body = "<h2>Webex Summary - Last Hour</h2>"
|
|
97
|
+
|
|
98
|
+
if mentions:
|
|
99
|
+
body += "<h3>Mentions</h3>"
|
|
100
|
+
grouped = {}
|
|
101
|
+
for msg in mentions:
|
|
102
|
+
space = msg.get("_spaceName", "Unknown Space")
|
|
103
|
+
grouped.setdefault(space, []).append(msg)
|
|
104
|
+
for space, msgs in grouped.items():
|
|
105
|
+
body += f"<h4>{space}</h4><ul>"
|
|
106
|
+
for msg in msgs:
|
|
107
|
+
sender = msg.get("personEmail", "unknown")
|
|
108
|
+
text = msg.get("text", "")[:200]
|
|
109
|
+
time = msg["created"][:16].replace("T", " ")
|
|
110
|
+
body += f"<li><b>{sender}</b> ({time}): {text}</li>"
|
|
111
|
+
body += "</ul>"
|
|
112
|
+
|
|
113
|
+
if dms:
|
|
114
|
+
body += "<h3>Direct Messages</h3><ul>"
|
|
115
|
+
for msg in dms:
|
|
116
|
+
sender = msg.get("personEmail", "unknown")
|
|
117
|
+
text = msg.get("text", "")[:200]
|
|
118
|
+
time = msg["created"][:16].replace("T", " ")
|
|
119
|
+
body += f"<li><b>{sender}</b> ({time}): {text}</li>"
|
|
120
|
+
body += "</ul>"
|
|
121
|
+
|
|
122
|
+
if not mentions and not dms:
|
|
123
|
+
body += "<p>No new mentions or direct messages in the last hour.</p>"
|
|
124
|
+
|
|
125
|
+
return body
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def send_email(to_email, subject, body):
|
|
129
|
+
"""Send email via Nylas SDK."""
|
|
130
|
+
api_key = os.environ.get("NYLAS_API_KEY")
|
|
131
|
+
grant_id = os.environ.get("NYLAS_GRANT_ID")
|
|
132
|
+
if not api_key or not grant_id:
|
|
133
|
+
sys.exit("Error: NYLAS_API_KEY and NYLAS_GRANT_ID environment variables required")
|
|
134
|
+
|
|
135
|
+
nylas = NylasClient(api_key=api_key)
|
|
136
|
+
nylas.messages.send(
|
|
137
|
+
grant_id,
|
|
138
|
+
request_body={
|
|
139
|
+
"to": [{"email": to_email}],
|
|
140
|
+
"subject": subject,
|
|
141
|
+
"body": body,
|
|
142
|
+
},
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def main():
|
|
147
|
+
parser = argparse.ArgumentParser(description="Webex Email Reminders")
|
|
148
|
+
parser.add_argument("--hours", type=float, default=1, help="Look back period in hours (default: 1)")
|
|
149
|
+
parser.add_argument("--to", help="Email address to send summary to")
|
|
150
|
+
parser.add_argument("--to-list", help="Text file with email addresses to send to (one per line)")
|
|
151
|
+
parser.add_argument("--contacts", help="Text file with email addresses to filter DMs (one per line)")
|
|
152
|
+
parser.add_argument("--dry-run", action="store_true", help="Print summary without sending email")
|
|
153
|
+
args = parser.parse_args()
|
|
154
|
+
|
|
155
|
+
if not args.to and not args.to_list:
|
|
156
|
+
parser.error("either --to or --to-list is required")
|
|
157
|
+
|
|
158
|
+
since = datetime.now(timezone.utc) - timedelta(hours=args.hours)
|
|
159
|
+
headers = get_webex_headers()
|
|
160
|
+
|
|
161
|
+
print(f"Checking Webex for activity since {since.isoformat()}...")
|
|
162
|
+
|
|
163
|
+
mentions = get_mentions(headers, since)
|
|
164
|
+
print(f" Found {len(mentions)} mention(s)")
|
|
165
|
+
|
|
166
|
+
dms = get_direct_messages(headers, since, args.contacts)
|
|
167
|
+
print(f" Found {len(dms)} direct message(s)")
|
|
168
|
+
|
|
169
|
+
if not mentions and not dms:
|
|
170
|
+
print("Nothing to report.")
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
body = format_email_body(mentions, dms)
|
|
174
|
+
subject = f"Webex Summary - {len(mentions)} mention(s), {len(dms)} DM(s)"
|
|
175
|
+
|
|
176
|
+
recipients = []
|
|
177
|
+
if args.to:
|
|
178
|
+
recipients.append(args.to)
|
|
179
|
+
if args.to_list and os.path.exists(args.to_list):
|
|
180
|
+
with open(args.to_list) as f:
|
|
181
|
+
recipients.extend(line.strip() for line in f if line.strip())
|
|
182
|
+
|
|
183
|
+
if args.dry_run:
|
|
184
|
+
print(f"\n--- DRY RUN ---\nTo: {', '.join(recipients)}\nSubject: {subject}\n\n{body}")
|
|
185
|
+
else:
|
|
186
|
+
for recipient in recipients:
|
|
187
|
+
send_email(recipient, subject, body)
|
|
188
|
+
print(f"Email sent to {', '.join(recipients)}")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
main()
|