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.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: webex-email-reminders
3
+ Version: 0.1.0
4
+ Summary: CLI tool that monitors Webex mentions and DMs, sends email summaries via Nylas
5
+ Requires-Python: >=3.9
6
+ Requires-Dist: nylas==6.2.0
7
+ Requires-Dist: requests==2.32.3
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: webex-email-reminders
3
+ Version: 0.1.0
4
+ Summary: CLI tool that monitors Webex mentions and DMs, sends email summaries via Nylas
5
+ Requires-Python: >=3.9
6
+ Requires-Dist: nylas==6.2.0
7
+ Requires-Dist: requests==2.32.3
@@ -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,2 @@
1
+ [console_scripts]
2
+ webex-reminders = webex_reminders:main
@@ -0,0 +1,2 @@
1
+ nylas==6.2.0
2
+ requests==2.32.3
@@ -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()