universal-mcp-applications 0.1.22__py3-none-any.whl → 0.1.39rc8__py3-none-any.whl
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.
Potentially problematic release.
This version of universal-mcp-applications might be problematic. Click here for more details.
- universal_mcp/applications/ahrefs/app.py +92 -238
- universal_mcp/applications/airtable/app.py +23 -122
- universal_mcp/applications/apollo/app.py +122 -475
- universal_mcp/applications/asana/app.py +605 -1755
- universal_mcp/applications/aws_s3/app.py +36 -103
- universal_mcp/applications/bill/app.py +644 -2055
- universal_mcp/applications/box/app.py +1246 -4159
- universal_mcp/applications/braze/app.py +410 -1476
- universal_mcp/applications/browser_use/README.md +15 -1
- universal_mcp/applications/browser_use/__init__.py +1 -0
- universal_mcp/applications/browser_use/app.py +94 -37
- universal_mcp/applications/cal_com_v2/app.py +207 -625
- universal_mcp/applications/calendly/app.py +103 -242
- universal_mcp/applications/canva/app.py +75 -140
- universal_mcp/applications/clickup/app.py +331 -798
- universal_mcp/applications/coda/app.py +240 -520
- universal_mcp/applications/confluence/app.py +497 -1285
- universal_mcp/applications/contentful/app.py +36 -151
- universal_mcp/applications/crustdata/app.py +42 -121
- universal_mcp/applications/dialpad/app.py +451 -924
- universal_mcp/applications/digitalocean/app.py +2071 -6082
- universal_mcp/applications/domain_checker/app.py +3 -54
- universal_mcp/applications/e2b/app.py +14 -64
- universal_mcp/applications/elevenlabs/app.py +9 -47
- universal_mcp/applications/exa/README.md +8 -4
- universal_mcp/applications/exa/app.py +408 -186
- universal_mcp/applications/falai/app.py +24 -101
- universal_mcp/applications/figma/app.py +91 -175
- universal_mcp/applications/file_system/app.py +2 -13
- universal_mcp/applications/firecrawl/app.py +186 -163
- universal_mcp/applications/fireflies/app.py +59 -281
- universal_mcp/applications/fpl/app.py +92 -529
- universal_mcp/applications/fpl/utils/fixtures.py +15 -49
- universal_mcp/applications/fpl/utils/helper.py +25 -89
- universal_mcp/applications/fpl/utils/league_utils.py +20 -64
- universal_mcp/applications/ghost_content/app.py +66 -175
- universal_mcp/applications/github/app.py +28 -65
- universal_mcp/applications/gong/app.py +140 -300
- universal_mcp/applications/google_calendar/app.py +26 -78
- universal_mcp/applications/google_docs/app.py +324 -354
- universal_mcp/applications/google_drive/app.py +194 -793
- universal_mcp/applications/google_gemini/app.py +29 -64
- universal_mcp/applications/google_mail/README.md +1 -0
- universal_mcp/applications/google_mail/app.py +93 -214
- universal_mcp/applications/google_searchconsole/app.py +25 -58
- universal_mcp/applications/google_sheet/app.py +174 -623
- universal_mcp/applications/google_sheet/helper.py +26 -53
- universal_mcp/applications/hashnode/app.py +57 -269
- universal_mcp/applications/heygen/app.py +77 -155
- universal_mcp/applications/http_tools/app.py +10 -32
- universal_mcp/applications/hubspot/README.md +1 -1
- universal_mcp/applications/hubspot/app.py +7508 -99
- universal_mcp/applications/jira/app.py +2419 -8334
- universal_mcp/applications/klaviyo/app.py +737 -1619
- universal_mcp/applications/linkedin/README.md +23 -4
- universal_mcp/applications/linkedin/app.py +861 -155
- universal_mcp/applications/mailchimp/app.py +696 -1851
- universal_mcp/applications/markitdown/app.py +8 -20
- universal_mcp/applications/miro/app.py +333 -815
- universal_mcp/applications/ms_teams/app.py +85 -207
- universal_mcp/applications/neon/app.py +144 -250
- universal_mcp/applications/notion/app.py +36 -51
- universal_mcp/applications/onedrive/README.md +24 -0
- universal_mcp/applications/onedrive/__init__.py +1 -0
- universal_mcp/applications/onedrive/app.py +316 -0
- universal_mcp/applications/openai/app.py +42 -165
- universal_mcp/applications/outlook/README.md +22 -9
- universal_mcp/applications/outlook/app.py +606 -262
- universal_mcp/applications/perplexity/README.md +2 -1
- universal_mcp/applications/perplexity/app.py +162 -20
- universal_mcp/applications/pipedrive/app.py +1021 -3331
- universal_mcp/applications/posthog/app.py +272 -541
- universal_mcp/applications/reddit/app.py +88 -204
- universal_mcp/applications/resend/app.py +41 -107
- universal_mcp/applications/retell/app.py +23 -50
- universal_mcp/applications/rocketlane/app.py +250 -963
- universal_mcp/applications/scraper/README.md +7 -4
- universal_mcp/applications/scraper/app.py +245 -283
- universal_mcp/applications/semanticscholar/app.py +36 -78
- universal_mcp/applications/semrush/app.py +43 -77
- universal_mcp/applications/sendgrid/app.py +826 -1576
- universal_mcp/applications/sentry/app.py +444 -1079
- universal_mcp/applications/serpapi/app.py +40 -143
- universal_mcp/applications/sharepoint/README.md +16 -14
- universal_mcp/applications/sharepoint/app.py +245 -154
- universal_mcp/applications/shopify/app.py +1743 -4479
- universal_mcp/applications/shortcut/app.py +272 -534
- universal_mcp/applications/slack/app.py +58 -109
- universal_mcp/applications/spotify/app.py +206 -405
- universal_mcp/applications/supabase/app.py +174 -283
- universal_mcp/applications/tavily/app.py +2 -2
- universal_mcp/applications/trello/app.py +853 -2816
- universal_mcp/applications/twilio/app.py +14 -50
- universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
- universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
- universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
- universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
- universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
- universal_mcp/applications/whatsapp/app.py +35 -186
- universal_mcp/applications/whatsapp/audio.py +2 -6
- universal_mcp/applications/whatsapp/whatsapp.py +17 -51
- universal_mcp/applications/whatsapp_business/app.py +86 -299
- universal_mcp/applications/wrike/app.py +80 -153
- universal_mcp/applications/yahoo_finance/app.py +19 -65
- universal_mcp/applications/youtube/app.py +120 -306
- universal_mcp/applications/zenquotes/app.py +4 -4
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/METADATA +4 -2
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/RECORD +113 -117
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/WHEEL +1 -1
- universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
- universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
- universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
- universal_mcp/applications/unipile/README.md +0 -28
- universal_mcp/applications/unipile/__init__.py +0 -1
- universal_mcp/applications/unipile/app.py +0 -1077
- {universal_mcp_applications-0.1.22.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
from urllib.parse import parse_qs, urlparse
|
|
3
|
-
|
|
4
3
|
from universal_mcp.applications.application import APIApplication
|
|
5
4
|
from universal_mcp.integrations import Integration
|
|
6
5
|
|
|
@@ -10,163 +9,146 @@ class OutlookApp(APIApplication):
|
|
|
10
9
|
super().__init__(name="outlook", integration=integration, **kwargs)
|
|
11
10
|
self.base_url = "https://graph.microsoft.com/v1.0"
|
|
12
11
|
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
user_id
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
async def _get_user_id(self) -> str:
|
|
13
|
+
"""Helper to get the userPrincipalName from the profile."""
|
|
14
|
+
user_info = await self.get_my_profile()
|
|
15
|
+
user_id = user_info.get("userPrincipalName")
|
|
16
|
+
if not user_id:
|
|
17
|
+
raise ValueError("Could not retrieve user ID from get_my_profile response.")
|
|
18
|
+
return user_id
|
|
19
|
+
|
|
20
|
+
async def reply_to_email(
|
|
21
|
+
self, message_id: str, comment: str, attachments: list[dict[str, Any]] | None = None
|
|
22
|
+
) -> dict[str, Any]:
|
|
20
23
|
"""
|
|
21
|
-
Replies to
|
|
24
|
+
Replies to a specific email message.
|
|
22
25
|
|
|
23
26
|
Args:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
message_id (str): The ID of the email message to reply to.
|
|
28
|
+
comment (str): The body of the reply.
|
|
29
|
+
attachments (list[dict[str, Any]], optional): A list of attachment objects to include in the reply.
|
|
30
|
+
Each attachment dictionary should conform to the Microsoft Graph API specification.
|
|
31
|
+
Example:
|
|
32
|
+
[
|
|
33
|
+
{
|
|
34
|
+
"@odata.type": "#microsoft.graph.fileAttachment",
|
|
35
|
+
"name": "attachment.txt",
|
|
36
|
+
"contentType": "text/plain",
|
|
37
|
+
"contentBytes": "SGVsbG8gV29ybGQh"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
28
40
|
|
|
29
41
|
Returns:
|
|
30
|
-
Any:
|
|
42
|
+
dict[str, Any]: A dictionary confirming the reply action.
|
|
31
43
|
|
|
32
44
|
Raises:
|
|
33
|
-
HTTPStatusError:
|
|
45
|
+
HTTPStatusError: If the API request fails.
|
|
46
|
+
ValueError: If message_id is missing.
|
|
34
47
|
|
|
35
48
|
Tags:
|
|
36
|
-
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
if
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"Could not retrieve user ID from get_current_user_profile response."
|
|
45
|
-
)
|
|
46
|
-
if message_id is None:
|
|
47
|
-
raise ValueError("Missing required parameter 'message-id'.")
|
|
48
|
-
request_body_data = None
|
|
49
|
-
request_body_data = {
|
|
50
|
-
"comment": comment,
|
|
51
|
-
"message": message,
|
|
52
|
-
}
|
|
53
|
-
request_body_data = {
|
|
54
|
-
k: v for k, v in request_body_data.items() if v is not None
|
|
55
|
-
}
|
|
49
|
+
important
|
|
50
|
+
"""
|
|
51
|
+
user_id = await self._get_user_id()
|
|
52
|
+
if not message_id:
|
|
53
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
54
|
+
request_body_data = {"comment": comment}
|
|
55
|
+
if attachments:
|
|
56
|
+
request_body_data["message"] = {"attachments": attachments}
|
|
56
57
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/reply"
|
|
57
|
-
|
|
58
|
-
response = self._post(
|
|
59
|
-
url,
|
|
60
|
-
data=request_body_data,
|
|
61
|
-
params=query_params,
|
|
62
|
-
content_type="application/json",
|
|
63
|
-
)
|
|
58
|
+
response = await self._apost(url, data=request_body_data, params={}, content_type="application/json")
|
|
64
59
|
return self._handle_response(response)
|
|
65
60
|
|
|
66
|
-
def
|
|
61
|
+
async def send_email(
|
|
67
62
|
self,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
subject: str,
|
|
64
|
+
body: str,
|
|
65
|
+
to_recipients: list[str],
|
|
66
|
+
cc_recipients: list[str] | None = None,
|
|
67
|
+
bcc_recipients: list[str] | None = None,
|
|
68
|
+
attachments: list[dict[str, Any]] | None = None,
|
|
69
|
+
body_content_type: str = "Text",
|
|
70
|
+
save_to_sent_items: bool = True,
|
|
71
|
+
) -> dict[str, Any]:
|
|
72
72
|
"""
|
|
73
|
-
Sends a new email
|
|
73
|
+
Sends a new email.
|
|
74
74
|
|
|
75
75
|
Args:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
subject (str): The subject of the email.
|
|
77
|
+
body (str): The body of the email.
|
|
78
|
+
to_recipients (list[str]): A list of email addresses for the 'To' recipients.
|
|
79
|
+
cc_recipients (list[str], optional): A list of email addresses for the 'Cc' recipients.
|
|
80
|
+
bcc_recipients (list[str], optional): A list of email addresses for the 'Bcc' recipients.
|
|
81
|
+
attachments (list[dict[str, Any]], optional): A list of attachment objects. See `reply_to_email` for an example.
|
|
82
|
+
body_content_type (str, optional): The content type of the email body, e.g., "Text" or "HTML". Defaults to "Text".
|
|
83
|
+
save_to_sent_items (bool, optional): Whether to save the email to the 'Sent Items' folder. Defaults to True.
|
|
79
84
|
|
|
80
85
|
Returns:
|
|
81
|
-
Any:
|
|
86
|
+
dict[str, Any]: A dictionary confirming the send action.
|
|
82
87
|
|
|
83
88
|
Raises:
|
|
84
|
-
HTTPStatusError:
|
|
89
|
+
HTTPStatusError: If the API request fails.
|
|
85
90
|
|
|
86
91
|
Tags:
|
|
87
|
-
|
|
88
|
-
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
raise ValueError(
|
|
95
|
-
"Could not retrieve user ID from get_current_user_profile response."
|
|
96
|
-
)
|
|
97
|
-
request_body_data = None
|
|
98
|
-
request_body_data = {
|
|
99
|
-
"message": message,
|
|
100
|
-
"saveToSentItems": saveToSentItems,
|
|
101
|
-
}
|
|
102
|
-
request_body_data = {
|
|
103
|
-
k: v for k, v in request_body_data.items() if v is not None
|
|
92
|
+
important
|
|
93
|
+
"""
|
|
94
|
+
user_id = await self._get_user_id()
|
|
95
|
+
message = {
|
|
96
|
+
"subject": subject,
|
|
97
|
+
"body": {"contentType": body_content_type, "content": body},
|
|
98
|
+
"toRecipients": [{"emailAddress": {"address": email}} for email in to_recipients],
|
|
104
99
|
}
|
|
100
|
+
if cc_recipients:
|
|
101
|
+
message["ccRecipients"] = [{"emailAddress": {"address": email}} for email in cc_recipients]
|
|
102
|
+
if bcc_recipients:
|
|
103
|
+
message["bccRecipients"] = [{"emailAddress": {"address": email}} for email in bcc_recipients]
|
|
104
|
+
if attachments:
|
|
105
|
+
message["attachments"] = attachments
|
|
106
|
+
request_body_data = {"message": message, "saveToSentItems": save_to_sent_items}
|
|
105
107
|
url = f"{self.base_url}/users/{user_id}/sendMail"
|
|
106
|
-
|
|
107
|
-
response = self._post(
|
|
108
|
-
url,
|
|
109
|
-
data=request_body_data,
|
|
110
|
-
params=query_params,
|
|
111
|
-
content_type="application/json",
|
|
112
|
-
)
|
|
108
|
+
response = await self._apost(url, data=request_body_data, params={}, content_type="application/json")
|
|
113
109
|
return self._handle_response(response)
|
|
114
110
|
|
|
115
|
-
def
|
|
111
|
+
async def get_email_folder(
|
|
116
112
|
self,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
includeHiddenFolders: str | None = None,
|
|
113
|
+
folder_id: str,
|
|
114
|
+
include_hidden: bool | None = None,
|
|
120
115
|
select: list[str] | None = None,
|
|
121
116
|
expand: list[str] | None = None,
|
|
122
|
-
) -> Any:
|
|
117
|
+
) -> dict[str, Any]:
|
|
123
118
|
"""
|
|
124
|
-
Retrieves a specific
|
|
119
|
+
Retrieves a specific email folder's metadata by its ID.
|
|
125
120
|
|
|
126
121
|
Args:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
expand (array): Expand related entities
|
|
122
|
+
folder_id (str): The unique identifier for the mail folder.
|
|
123
|
+
include_hidden (bool, optional): If true, includes hidden folders in the results.
|
|
124
|
+
select (list[str], optional): A list of properties to return.
|
|
125
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
132
126
|
|
|
133
127
|
Returns:
|
|
134
|
-
Any:
|
|
128
|
+
dict[str, Any]: A dictionary containing the mail folder's metadata.
|
|
135
129
|
|
|
136
130
|
Raises:
|
|
137
|
-
HTTPStatusError:
|
|
138
|
-
|
|
131
|
+
HTTPStatusError: If the API request fails.
|
|
132
|
+
ValueError: If folder_id is missing.
|
|
139
133
|
Tags:
|
|
140
|
-
|
|
141
|
-
"""
|
|
142
|
-
|
|
143
|
-
if
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
"Could not retrieve user ID from get_current_user_profile response."
|
|
149
|
-
)
|
|
150
|
-
if mailFolder_id is None:
|
|
151
|
-
raise ValueError("Missing required parameter 'mailFolder-id'.")
|
|
152
|
-
url = f"{self.base_url}/users/{user_id}/mailFolders/{mailFolder_id}"
|
|
134
|
+
important
|
|
135
|
+
"""
|
|
136
|
+
user_id = await self._get_user_id()
|
|
137
|
+
if not folder_id:
|
|
138
|
+
raise ValueError("Missing required parameter 'folder_id'.")
|
|
139
|
+
url = f"{self.base_url}/users/{user_id}/mailFolders/{folder_id}"
|
|
140
|
+
select_str = ",".join(select) if select else None
|
|
141
|
+
expand_str = ",".join(expand) if expand else None
|
|
153
142
|
query_params = {
|
|
154
|
-
k: v
|
|
155
|
-
for k, v in [
|
|
156
|
-
("includeHiddenFolders", includeHiddenFolders),
|
|
157
|
-
("$select", select),
|
|
158
|
-
("$expand", expand),
|
|
159
|
-
]
|
|
160
|
-
if v is not None
|
|
143
|
+
k: v for k, v in [("includeHiddenFolders", include_hidden), ("$select", select_str), ("$expand", expand_str)] if v is not None
|
|
161
144
|
}
|
|
162
|
-
response = self.
|
|
145
|
+
response = await self._aget(url, params=query_params)
|
|
163
146
|
return self._handle_response(response)
|
|
164
147
|
|
|
165
|
-
def
|
|
148
|
+
async def list_emails(
|
|
166
149
|
self,
|
|
167
|
-
user_id: str | None = None,
|
|
168
150
|
select: list[str] = ["bodyPreview"],
|
|
169
|
-
|
|
151
|
+
include_hidden: bool | None = None,
|
|
170
152
|
top: int | None = None,
|
|
171
153
|
skip: int | None = None,
|
|
172
154
|
search: str | None = None,
|
|
@@ -176,56 +158,44 @@ class OutlookApp(APIApplication):
|
|
|
176
158
|
expand: list[str] | None = None,
|
|
177
159
|
) -> dict[str, Any]:
|
|
178
160
|
"""
|
|
179
|
-
Retrieves a list of
|
|
161
|
+
Retrieves a list of emails from a user's mailbox.
|
|
180
162
|
|
|
181
163
|
Args:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
includeHiddenMessages (boolean): Include Hidden Messages
|
|
192
|
-
top (integer): Specify the number of items to be included in the result Example: '50'.
|
|
193
|
-
skip (integer): Specify the number of items to skip in the result Example: '10'.
|
|
194
|
-
search (string): Search items by search phrases
|
|
195
|
-
filter (string): Filter items by property values
|
|
196
|
-
count (boolean): Include count of items
|
|
197
|
-
orderby (array): Order items by property values
|
|
198
|
-
expand (array): Expand related entities
|
|
164
|
+
select (list[str], optional): A list of properties to return for each email. Defaults to ['bodyPreview'].
|
|
165
|
+
include_hidden (bool, optional): If true, includes hidden messages.
|
|
166
|
+
top (int, optional): The maximum number of emails to return.
|
|
167
|
+
skip (int, optional): The number of emails to skip. Cannot be used with 'search'.
|
|
168
|
+
search (str, optional): A search query. Cannot be used with 'filter', 'orderby', or 'skip'.
|
|
169
|
+
filter (str, optional): A filter query. Cannot be used with 'search'.
|
|
170
|
+
count (bool, optional): If true, includes the total count of emails in the response.
|
|
171
|
+
orderby (list[str], optional): A list of properties to sort the results by. Cannot be used with 'search'.
|
|
172
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
199
173
|
|
|
200
174
|
Returns:
|
|
201
|
-
dict[str, Any]:
|
|
175
|
+
dict[str, Any]: A dictionary containing a list of emails and pagination information.
|
|
202
176
|
|
|
203
177
|
Raises:
|
|
204
|
-
|
|
205
|
-
|
|
178
|
+
ValueError: If incompatible parameters are used together (e.g., 'search' with 'filter').
|
|
179
|
+
HTTPStatusError: If the API request fails.
|
|
206
180
|
Tags:
|
|
207
|
-
|
|
181
|
+
important
|
|
208
182
|
"""
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
183
|
+
if search:
|
|
184
|
+
if filter:
|
|
185
|
+
raise ValueError("The 'search' parameter cannot be used with 'filter'.")
|
|
186
|
+
if orderby:
|
|
187
|
+
raise ValueError("The 'search' parameter cannot be used with 'orderby'.")
|
|
188
|
+
if skip:
|
|
189
|
+
raise ValueError("The 'search' parameter cannot be used with 'skip'. Use pagination via @odata.nextLink instead.")
|
|
190
|
+
user_id = await self._get_user_id()
|
|
218
191
|
url = f"{self.base_url}/users/{user_id}/messages"
|
|
219
|
-
|
|
220
|
-
# Handle list parameters by joining with commas
|
|
221
192
|
select_str = ",".join(select) if select else None
|
|
222
193
|
orderby_str = ",".join(orderby) if orderby else None
|
|
223
194
|
expand_str = ",".join(expand) if expand else None
|
|
224
|
-
|
|
225
195
|
query_params = {
|
|
226
196
|
k: v
|
|
227
197
|
for k, v in [
|
|
228
|
-
("includeHiddenMessages",
|
|
198
|
+
("includeHiddenMessages", include_hidden),
|
|
229
199
|
("$top", top),
|
|
230
200
|
("$skip", skip),
|
|
231
201
|
("$search", search),
|
|
@@ -237,96 +207,72 @@ class OutlookApp(APIApplication):
|
|
|
237
207
|
]
|
|
238
208
|
if v is not None
|
|
239
209
|
}
|
|
240
|
-
|
|
241
|
-
response = self._get(url, params=query_params)
|
|
210
|
+
response = await self._aget(url, params=query_params)
|
|
242
211
|
return self._handle_response(response)
|
|
243
212
|
|
|
244
|
-
def
|
|
213
|
+
async def get_email(
|
|
245
214
|
self,
|
|
246
215
|
message_id: str,
|
|
247
|
-
|
|
248
|
-
includeHiddenMessages: str | None = None,
|
|
216
|
+
include_hidden: bool | None = None,
|
|
249
217
|
select: list[str] | None = None,
|
|
250
218
|
expand: list[str] | None = None,
|
|
251
|
-
) -> Any:
|
|
219
|
+
) -> dict[str, Any]:
|
|
252
220
|
"""
|
|
253
|
-
Retrieves a specific email
|
|
221
|
+
Retrieves a specific email by its ID.
|
|
254
222
|
|
|
255
223
|
Args:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
expand (array): Expand related entities
|
|
224
|
+
message_id (str): The unique identifier for the email.
|
|
225
|
+
include_hidden (bool, optional): If true, includes hidden messages.
|
|
226
|
+
select (list[str], optional): A list of properties to return.
|
|
227
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
261
228
|
|
|
262
229
|
Returns:
|
|
263
|
-
Any:
|
|
230
|
+
dict[str, Any]: A dictionary containing the email's details.
|
|
264
231
|
|
|
265
232
|
Raises:
|
|
266
|
-
HTTPStatusError:
|
|
267
|
-
|
|
233
|
+
HTTPStatusError: If the API request fails.
|
|
234
|
+
ValueError: If user_id cannot be retrieved or message_id is missing.
|
|
268
235
|
Tags:
|
|
269
|
-
|
|
270
|
-
"""
|
|
271
|
-
|
|
272
|
-
if
|
|
273
|
-
|
|
274
|
-
user_id = user_info.get("userPrincipalName")
|
|
275
|
-
if not user_id:
|
|
276
|
-
raise ValueError(
|
|
277
|
-
"Could not retrieve user ID from get_current_user_profile response."
|
|
278
|
-
)
|
|
279
|
-
if message_id is None:
|
|
280
|
-
raise ValueError("Missing required parameter 'message-id'.")
|
|
236
|
+
important
|
|
237
|
+
"""
|
|
238
|
+
user_id = await self._get_user_id()
|
|
239
|
+
if not message_id:
|
|
240
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
281
241
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}"
|
|
242
|
+
select_str = ",".join(select) if select else None
|
|
243
|
+
expand_str = ",".join(expand) if expand else None
|
|
282
244
|
query_params = {
|
|
283
|
-
k: v
|
|
284
|
-
for k, v in [
|
|
285
|
-
("includeHiddenMessages", includeHiddenMessages),
|
|
286
|
-
("$select", select),
|
|
287
|
-
("$expand", expand),
|
|
288
|
-
]
|
|
289
|
-
if v is not None
|
|
245
|
+
k: v for k, v in [("includeHiddenMessages", include_hidden), ("$select", select_str), ("$expand", expand_str)] if v is not None
|
|
290
246
|
}
|
|
291
|
-
response = self.
|
|
247
|
+
response = await self._aget(url, params=query_params)
|
|
292
248
|
return self._handle_response(response)
|
|
293
249
|
|
|
294
|
-
def
|
|
250
|
+
async def delete_email(self, message_id: str) -> dict[str, Any]:
|
|
295
251
|
"""
|
|
296
|
-
Permanently deletes a specific email
|
|
252
|
+
Permanently deletes a specific email by its ID.
|
|
297
253
|
|
|
298
254
|
Args:
|
|
299
|
-
|
|
300
|
-
message_id (string): message-id
|
|
255
|
+
message_id (str): The unique identifier for the email to be deleted.
|
|
301
256
|
|
|
302
257
|
Returns:
|
|
303
|
-
Any:
|
|
258
|
+
dict[str, Any]: A dictionary confirming the deletion.
|
|
304
259
|
|
|
305
260
|
Raises:
|
|
306
|
-
HTTPStatusError:
|
|
307
|
-
|
|
261
|
+
HTTPStatusError: If the API request fails.
|
|
262
|
+
ValueError: If message_id is missing.
|
|
308
263
|
Tags:
|
|
309
|
-
|
|
310
|
-
"""
|
|
311
|
-
|
|
312
|
-
if
|
|
313
|
-
|
|
314
|
-
user_id = user_info.get("userPrincipalName")
|
|
315
|
-
if not user_id:
|
|
316
|
-
raise ValueError(
|
|
317
|
-
"Could not retrieve user ID from get_current_user_profile response."
|
|
318
|
-
)
|
|
319
|
-
if message_id is None:
|
|
320
|
-
raise ValueError("Missing required parameter 'message-id'.")
|
|
264
|
+
important
|
|
265
|
+
"""
|
|
266
|
+
user_id = await self._get_user_id()
|
|
267
|
+
if not message_id:
|
|
268
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
321
269
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}"
|
|
322
|
-
|
|
323
|
-
response = self._delete(url, params=query_params)
|
|
270
|
+
response = await self._adelete(url, params={})
|
|
324
271
|
return self._handle_response(response)
|
|
325
272
|
|
|
326
|
-
def
|
|
273
|
+
async def list_email_attachments(
|
|
327
274
|
self,
|
|
328
275
|
message_id: str,
|
|
329
|
-
user_id: str | None = None,
|
|
330
276
|
top: int | None = None,
|
|
331
277
|
skip: int | None = None,
|
|
332
278
|
search: str | None = None,
|
|
@@ -337,40 +283,42 @@ class OutlookApp(APIApplication):
|
|
|
337
283
|
expand: list[str] | None = None,
|
|
338
284
|
) -> dict[str, Any]:
|
|
339
285
|
"""
|
|
340
|
-
Retrieves attachments for a specific email
|
|
286
|
+
Retrieves attachments for a specific email.
|
|
341
287
|
|
|
342
288
|
Args:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
expand (array): Expand related entities
|
|
289
|
+
message_id (str): The unique identifier for the email.
|
|
290
|
+
top (int, optional): The maximum number of attachments to return.
|
|
291
|
+
skip (int, optional): The number of attachments to skip. Cannot be used with 'search'.
|
|
292
|
+
search (str, optional): A search query. Cannot be used with 'filter', 'orderby', or 'skip'.
|
|
293
|
+
filter (str, optional): A filter query. Cannot be used with 'search'.
|
|
294
|
+
count (bool, optional): If true, includes the total count of attachments.
|
|
295
|
+
orderby (list[str], optional): A list of properties to sort by. Cannot be used with 'search'.
|
|
296
|
+
select (list[str], optional): A list of properties to return.
|
|
297
|
+
expand (list[str], optional): A list of related entities to expand.
|
|
353
298
|
|
|
354
299
|
Returns:
|
|
355
|
-
dict[str, Any]:
|
|
300
|
+
dict[str, Any]: A dictionary containing a list of attachments.
|
|
356
301
|
|
|
357
302
|
Raises:
|
|
358
|
-
|
|
359
|
-
|
|
303
|
+
ValueError: If incompatible parameters are used together.
|
|
304
|
+
HTTPStatusError: If the API request fails.
|
|
360
305
|
Tags:
|
|
361
|
-
|
|
362
|
-
"""
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if message_id
|
|
372
|
-
raise ValueError("Missing required parameter '
|
|
306
|
+
important
|
|
307
|
+
"""
|
|
308
|
+
if search:
|
|
309
|
+
if filter:
|
|
310
|
+
raise ValueError("The 'search' parameter cannot be used with 'filter'.")
|
|
311
|
+
if orderby:
|
|
312
|
+
raise ValueError("The 'search' parameter cannot be used with 'orderby'.")
|
|
313
|
+
if skip:
|
|
314
|
+
raise ValueError("The 'search' parameter cannot be used with 'skip'. Use pagination via @odata.nextLink instead.")
|
|
315
|
+
user_id = await self._get_user_id()
|
|
316
|
+
if not message_id:
|
|
317
|
+
raise ValueError("Missing required parameter 'message_id'.")
|
|
373
318
|
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/attachments"
|
|
319
|
+
orderby_str = ",".join(orderby) if orderby else None
|
|
320
|
+
select_str = ",".join(select) if select else None
|
|
321
|
+
expand_str = ",".join(expand) if expand else None
|
|
374
322
|
query_params = {
|
|
375
323
|
k: v
|
|
376
324
|
for k, v in [
|
|
@@ -379,64 +327,460 @@ class OutlookApp(APIApplication):
|
|
|
379
327
|
("$search", search),
|
|
380
328
|
("$filter", filter),
|
|
381
329
|
("$count", count),
|
|
382
|
-
("$orderby",
|
|
383
|
-
("$select",
|
|
384
|
-
("$expand",
|
|
330
|
+
("$orderby", orderby_str),
|
|
331
|
+
("$select", select_str),
|
|
332
|
+
("$expand", expand_str),
|
|
385
333
|
]
|
|
386
334
|
if v is not None
|
|
387
335
|
}
|
|
388
|
-
response = self.
|
|
336
|
+
response = await self._aget(url, params=query_params)
|
|
389
337
|
return self._handle_response(response)
|
|
390
338
|
|
|
391
|
-
def
|
|
339
|
+
async def get_attachment(self, message_id: str, attachment_id: str) -> dict[str, Any]:
|
|
340
|
+
"""
|
|
341
|
+
Retrieves a specific attachment from an email message and formats it as a dictionary.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
message_id (str): The ID of the email message.
|
|
345
|
+
attachment_id (str): The ID of the attachment.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
dict[str, Any]: A dictionary containing the attachment details:
|
|
349
|
+
- 'type' (str): The general type of the attachment (e.g., "image", "audio", "video", "file").
|
|
350
|
+
- 'data' (str): The base64 encoded content of the attachment.
|
|
351
|
+
- 'mime_type' (str): The MIME type of the attachment.
|
|
352
|
+
- 'file_name' (str): The name of the attachment file.
|
|
353
|
+
Tags:
|
|
354
|
+
important
|
|
355
|
+
"""
|
|
356
|
+
user_id = await self._get_user_id()
|
|
357
|
+
if not message_id or not attachment_id:
|
|
358
|
+
raise ValueError("Missing required parameter 'message_id' or 'attachment_id'.")
|
|
359
|
+
url = f"{self.base_url}/users/{user_id}/messages/{message_id}/attachments/{attachment_id}"
|
|
360
|
+
response = await self._aget(url, params={})
|
|
361
|
+
attachment_data = self._handle_response(response)
|
|
362
|
+
content_type = attachment_data.get("contentType", "application/octet-stream")
|
|
363
|
+
attachment_type = content_type.split("/")[0] if "/" in content_type else "file"
|
|
364
|
+
if attachment_type not in ["image", "audio", "video", "text"]:
|
|
365
|
+
attachment_type = "file"
|
|
366
|
+
return {
|
|
367
|
+
"type": attachment_type,
|
|
368
|
+
"data": attachment_data.get("contentBytes"),
|
|
369
|
+
"mime_type": content_type,
|
|
370
|
+
"file_name": attachment_data.get("name"),
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async def get_my_profile(self) -> dict[str, Any]:
|
|
374
|
+
"""
|
|
375
|
+
Fetches the userPrincipalName for the currently authenticated user.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
dict[str, Any]: A dictionary containing the user's principal name.
|
|
379
|
+
|
|
380
|
+
Raises:
|
|
381
|
+
HTTPStatusError: If the API request fails.
|
|
382
|
+
"""
|
|
383
|
+
url = f"{self.base_url}/me"
|
|
384
|
+
query_params = {"$select": "userPrincipalName"}
|
|
385
|
+
response = await self._aget(url, params=query_params)
|
|
386
|
+
return self._handle_response(response)
|
|
387
|
+
|
|
388
|
+
async def list_calendars(
|
|
392
389
|
self,
|
|
390
|
+
top: int | None = None,
|
|
391
|
+
skip: int | None = None,
|
|
392
|
+
select: list[str] | None = None,
|
|
393
393
|
) -> dict[str, Any]:
|
|
394
394
|
"""
|
|
395
|
-
|
|
395
|
+
Retrieves a list of calendars for the user.
|
|
396
396
|
|
|
397
|
+
Args:
|
|
398
|
+
top (int, optional): The maximum number of calendars to return.
|
|
399
|
+
skip (int, optional): The number of calendars to skip.
|
|
400
|
+
select (list[str], optional): A list of properties to return for each calendar.
|
|
397
401
|
|
|
398
402
|
Returns:
|
|
399
|
-
dict[str, Any]:
|
|
403
|
+
dict[str, Any]: A dictionary containing a list of calendars.
|
|
400
404
|
|
|
401
|
-
|
|
402
|
-
|
|
405
|
+
Tags:
|
|
406
|
+
important
|
|
407
|
+
"""
|
|
408
|
+
user_id = await self._get_user_id()
|
|
409
|
+
url = f"{self.base_url}/users/{user_id}/calendars"
|
|
410
|
+
select_str = ",".join(select) if select else None
|
|
411
|
+
query_params = {k: v for k, v in [("$top", top), ("$skip", skip), ("$select", select_str)] if v is not None}
|
|
412
|
+
response = await self._aget(url, params=query_params)
|
|
413
|
+
return self._handle_response(response)
|
|
414
|
+
|
|
415
|
+
async def get_calendar(
|
|
416
|
+
self, calendar_id: str, select: list[str] | None = None
|
|
417
|
+
) -> dict[str, Any]:
|
|
418
|
+
"""
|
|
419
|
+
Retrieves a specific calendar by its ID.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
calendar_id (str): The unique identifier for the calendar.
|
|
423
|
+
select (list[str], optional): A list of properties to return.
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
dict[str, Any]: A dictionary containing the calendar's details.
|
|
403
427
|
|
|
404
428
|
Tags:
|
|
405
|
-
|
|
429
|
+
important
|
|
406
430
|
"""
|
|
407
|
-
|
|
431
|
+
user_id = await self._get_user_id()
|
|
432
|
+
url = f"{self.base_url}/users/{user_id}/calendars/{calendar_id}"
|
|
433
|
+
select_str = ",".join(select) if select else None
|
|
434
|
+
query_params = {"$select": select_str} if select_str else {}
|
|
435
|
+
response = await self._aget(url, params=query_params)
|
|
436
|
+
return self._handle_response(response)
|
|
437
|
+
|
|
438
|
+
async def create_calendar(self, name: str) -> dict[str, Any]:
|
|
439
|
+
"""
|
|
440
|
+
Creates a new calendar for the user.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
name (str): The name of the new calendar.
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
dict[str, Any]: A dictionary containing the created calendar's details.
|
|
447
|
+
|
|
448
|
+
Tags:
|
|
449
|
+
important
|
|
450
|
+
"""
|
|
451
|
+
user_id = await self._get_user_id()
|
|
452
|
+
url = f"{self.base_url}/users/{user_id}/calendars"
|
|
453
|
+
request_body = {"name": name}
|
|
454
|
+
response = await self._apost(url, data=request_body, params={}, content_type="application/json")
|
|
455
|
+
return self._handle_response(response)
|
|
456
|
+
|
|
457
|
+
async def update_calendar(self, calendar_id: str, name: str) -> dict[str, Any]:
|
|
458
|
+
"""
|
|
459
|
+
Updates an existing calendar's name.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
calendar_id (str): The unique identifier for the calendar.
|
|
463
|
+
name (str): The new name for the calendar.
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
dict[str, Any]: A dictionary containing the updated calendar's details.
|
|
467
|
+
|
|
468
|
+
Tags:
|
|
469
|
+
important
|
|
470
|
+
"""
|
|
471
|
+
user_id = await self._get_user_id()
|
|
472
|
+
url = f"{self.base_url}/users/{user_id}/calendars/{calendar_id}"
|
|
473
|
+
request_body = {"name": name}
|
|
474
|
+
response = await self._apatch(url, data=request_body, params={}, content_type="application/json")
|
|
475
|
+
return self._handle_response(response)
|
|
476
|
+
|
|
477
|
+
async def delete_calendar(self, calendar_id: str) -> dict[str, Any]:
|
|
478
|
+
"""
|
|
479
|
+
Deletes a specific calendar.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
calendar_id (str): The unique identifier for the calendar to delete.
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
dict[str, Any]: A dictionary confirming the deletion.
|
|
486
|
+
|
|
487
|
+
Tags:
|
|
488
|
+
important
|
|
489
|
+
"""
|
|
490
|
+
user_id = await self._get_user_id()
|
|
491
|
+
url = f"{self.base_url}/users/{user_id}/calendars/{calendar_id}"
|
|
492
|
+
response = await self._adelete(url, params={})
|
|
493
|
+
return self._handle_response(response)
|
|
494
|
+
|
|
495
|
+
async def list_events(
|
|
496
|
+
self,
|
|
497
|
+
calendar_id: str | None = None,
|
|
498
|
+
top: int | None = None,
|
|
499
|
+
skip: int | None = None,
|
|
500
|
+
filter: str | None = None,
|
|
501
|
+
select: list[str] | None = None,
|
|
502
|
+
orderby: list[str] | None = None,
|
|
503
|
+
) -> dict[str, Any]:
|
|
504
|
+
"""
|
|
505
|
+
Retrieves a list of events from a calendar or the user's default calendar.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
calendar_id (str, optional): The ID of the calendar. If not provided, the default calendar is used.
|
|
509
|
+
top (int, optional): The maximum number of events to return.
|
|
510
|
+
skip (int, optional): The number of events to skip.
|
|
511
|
+
filter (str, optional): A filter query (e.g., "start/dateTime ge '2023-01-01T00:00:00Z'").
|
|
512
|
+
select (list[str], optional): A list of properties to return for each event.
|
|
513
|
+
orderby (list[str], optional): A list of properties to sort the results by.
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
dict[str, Any]: A dictionary containing a list of events.
|
|
517
|
+
|
|
518
|
+
Tags:
|
|
519
|
+
important
|
|
520
|
+
"""
|
|
521
|
+
user_id = await self._get_user_id()
|
|
522
|
+
if calendar_id:
|
|
523
|
+
url = f"{self.base_url}/users/{user_id}/calendars/{calendar_id}/events"
|
|
524
|
+
else:
|
|
525
|
+
url = f"{self.base_url}/users/{user_id}/calendar/events"
|
|
526
|
+
select_str = ",".join(select) if select else None
|
|
527
|
+
orderby_str = ",".join(orderby) if orderby else None
|
|
408
528
|
query_params = {
|
|
409
|
-
|
|
529
|
+
k: v
|
|
530
|
+
for k, v in [("$top", top), ("$skip", skip), ("$filter", filter), ("$select", select_str), ("$orderby", orderby_str)]
|
|
531
|
+
if v is not None
|
|
410
532
|
}
|
|
411
|
-
response = self.
|
|
533
|
+
response = await self._aget(url, params=query_params)
|
|
412
534
|
return self._handle_response(response)
|
|
413
535
|
|
|
414
|
-
def
|
|
536
|
+
async def get_event(
|
|
537
|
+
self, event_id: str, select: list[str] | None = None
|
|
538
|
+
) -> dict[str, Any]:
|
|
415
539
|
"""
|
|
416
|
-
|
|
540
|
+
Retrieves a specific event by its ID.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
event_id (str): The unique identifier for the event.
|
|
544
|
+
select (list[str], optional): A list of properties to return.
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
dict[str, Any]: A dictionary containing the event's details.
|
|
548
|
+
|
|
549
|
+
Tags:
|
|
550
|
+
important
|
|
551
|
+
"""
|
|
552
|
+
user_id = await self._get_user_id()
|
|
553
|
+
url = f"{self.base_url}/users/{user_id}/events/{event_id}"
|
|
554
|
+
select_str = ",".join(select) if select else None
|
|
555
|
+
query_params = {"$select": select_str} if select_str else {}
|
|
556
|
+
response = await self._aget(url, params=query_params)
|
|
557
|
+
return self._handle_response(response)
|
|
558
|
+
|
|
559
|
+
async def create_event(
|
|
560
|
+
self,
|
|
561
|
+
subject: str,
|
|
562
|
+
start_datetime: str,
|
|
563
|
+
end_datetime: str,
|
|
564
|
+
start_timezone: str = "UTC",
|
|
565
|
+
end_timezone: str = "UTC",
|
|
566
|
+
body: str | None = None,
|
|
567
|
+
body_content_type: str = "HTML",
|
|
568
|
+
location_display_name: str | None = None,
|
|
569
|
+
attendees: list[dict[str, Any]] | None = None,
|
|
570
|
+
calendar_id: str | None = None,
|
|
571
|
+
**kwargs,
|
|
572
|
+
) -> dict[str, Any]:
|
|
573
|
+
"""
|
|
574
|
+
Creates a new event in a calendar.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
subject (str): The subject of the event.
|
|
578
|
+
start_datetime (str): The start time of the event (ISO 8601 string, e.g., '2023-12-25T09:00:00').
|
|
579
|
+
end_datetime (str): The end time of the event (ISO 8601 string, e.g., '2023-12-25T10:00:00').
|
|
580
|
+
start_timezone (str, optional): The timezone for the start time. Defaults to 'UTC'.
|
|
581
|
+
end_timezone (str, optional): The timezone for the end time. Defaults to 'UTC'.
|
|
582
|
+
body (str, optional): The body content of the event.
|
|
583
|
+
body_content_type (str, optional): The content type of the body (Text or HTML). Defaults to 'HTML'.
|
|
584
|
+
location_display_name (str, optional): The display name for the event location.
|
|
585
|
+
attendees (list[dict[str, Any]], optional): A list of attendee objects.
|
|
586
|
+
Example attendee: {"type": "required", "emailAddress": {"address": "bob@example.com", "name": "Bob"}}
|
|
587
|
+
calendar_id (str, optional): The ID of the calendar. If not provided, the default calendar is used.
|
|
588
|
+
**kwargs: Additional properties for the event (e.g., isOnlineMeeting, reminderMinutesBeforeStart).
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
dict[str, Any]: A dictionary containing the created event's details.
|
|
592
|
+
|
|
593
|
+
Tags:
|
|
594
|
+
important
|
|
595
|
+
"""
|
|
596
|
+
user_id = await self._get_user_id()
|
|
597
|
+
if calendar_id:
|
|
598
|
+
url = f"{self.base_url}/users/{user_id}/calendars/{calendar_id}/events"
|
|
599
|
+
else:
|
|
600
|
+
url = f"{self.base_url}/users/{user_id}/calendar/events"
|
|
601
|
+
request_body = {
|
|
602
|
+
"subject": subject,
|
|
603
|
+
"start": {"dateTime": start_datetime, "timeZone": start_timezone},
|
|
604
|
+
"end": {"dateTime": end_datetime, "timeZone": end_timezone},
|
|
605
|
+
}
|
|
606
|
+
if body:
|
|
607
|
+
request_body["body"] = {"contentType": body_content_type, "content": body}
|
|
608
|
+
if location_display_name:
|
|
609
|
+
request_body["location"] = {"displayName": location_display_name}
|
|
610
|
+
if attendees:
|
|
611
|
+
request_body["attendees"] = attendees
|
|
612
|
+
request_body.update(kwargs)
|
|
613
|
+
response = await self._apost(url, data=request_body, params={}, content_type="application/json")
|
|
614
|
+
return self._handle_response(response)
|
|
615
|
+
|
|
616
|
+
async def update_event(
|
|
617
|
+
self,
|
|
618
|
+
event_id: str,
|
|
619
|
+
**kwargs,
|
|
620
|
+
) -> dict[str, Any]:
|
|
621
|
+
"""
|
|
622
|
+
Updates an existing event.
|
|
623
|
+
|
|
624
|
+
Args:
|
|
625
|
+
event_id (str): The unique identifier for the event.
|
|
626
|
+
**kwargs: Event properties to update (e.g., subject, start, end, body, attendees).
|
|
627
|
+
For start/end, use nested dictionaries: start={"dateTime": "...", "timeZone": "..."}.
|
|
628
|
+
|
|
629
|
+
Returns:
|
|
630
|
+
dict[str, Any]: A dictionary containing the updated event's details.
|
|
631
|
+
|
|
632
|
+
Tags:
|
|
633
|
+
important
|
|
634
|
+
"""
|
|
635
|
+
user_id = await self._get_user_id()
|
|
636
|
+
url = f"{self.base_url}/users/{user_id}/events/{event_id}"
|
|
637
|
+
response = await self._apatch(url, data=kwargs, params={}, content_type="application/json")
|
|
638
|
+
return self._handle_response(response)
|
|
639
|
+
|
|
640
|
+
async def delete_event(self, event_id: str) -> dict[str, Any]:
|
|
641
|
+
"""
|
|
642
|
+
Deletes a specific event.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
event_id (str): The unique identifier for the event to delete.
|
|
646
|
+
|
|
647
|
+
Returns:
|
|
648
|
+
dict[str, Any]: A dictionary confirming the deletion.
|
|
649
|
+
|
|
650
|
+
Tags:
|
|
651
|
+
important
|
|
652
|
+
"""
|
|
653
|
+
user_id = await self._get_user_id()
|
|
654
|
+
url = f"{self.base_url}/users/{user_id}/events/{event_id}"
|
|
655
|
+
response = await self._adelete(url, params={})
|
|
656
|
+
return self._handle_response(response)
|
|
657
|
+
|
|
658
|
+
async def list_calendar_view(
|
|
659
|
+
self,
|
|
660
|
+
start_datetime: str,
|
|
661
|
+
end_datetime: str,
|
|
662
|
+
calendar_id: str | None = None,
|
|
663
|
+
top: int | None = None,
|
|
664
|
+
skip: int | None = None,
|
|
665
|
+
select: list[str] | None = None,
|
|
666
|
+
) -> dict[str, Any]:
|
|
667
|
+
"""
|
|
668
|
+
Retrieves events between a start and end time (includes expanded recurring events).
|
|
669
|
+
|
|
670
|
+
Args:
|
|
671
|
+
start_datetime (str): The start of the time range (ISO 8601, e.g., '2023-12-25T00:00:00Z').
|
|
672
|
+
end_datetime (str): The end of the time range (ISO 8601, e.g., '2023-12-26T00:00:00Z').
|
|
673
|
+
calendar_id (str, optional): The ID of the calendar. If not provided, the default calendar is used.
|
|
674
|
+
top (int, optional): The maximum number of events to return.
|
|
675
|
+
skip (int, optional): The number of events to skip.
|
|
676
|
+
select (list[str], optional): A list of properties to return for each event.
|
|
677
|
+
|
|
678
|
+
Returns:
|
|
679
|
+
dict[str, Any]: A dictionary containing a list of event instances.
|
|
680
|
+
|
|
681
|
+
Tags:
|
|
682
|
+
important
|
|
683
|
+
"""
|
|
684
|
+
user_id = await self._get_user_id()
|
|
685
|
+
if calendar_id:
|
|
686
|
+
url = f"{self.base_url}/users/{user_id}/calendars/{calendar_id}/calendarView"
|
|
687
|
+
else:
|
|
688
|
+
url = f"{self.base_url}/users/{user_id}/calendar/calendarView"
|
|
689
|
+
select_str = ",".join(select) if select else None
|
|
690
|
+
query_params = {
|
|
691
|
+
"startDateTime": start_datetime,
|
|
692
|
+
"endDateTime": end_datetime,
|
|
693
|
+
}
|
|
694
|
+
if top is not None:
|
|
695
|
+
query_params["$top"] = top
|
|
696
|
+
if skip is not None:
|
|
697
|
+
query_params["$skip"] = skip
|
|
698
|
+
if select_str:
|
|
699
|
+
query_params["$select"] = select_str
|
|
700
|
+
response = await self._aget(url, params=query_params)
|
|
701
|
+
return self._handle_response(response)
|
|
702
|
+
|
|
703
|
+
async def get_schedule(
|
|
704
|
+
self,
|
|
705
|
+
schedules: list[str],
|
|
706
|
+
start_datetime: str,
|
|
707
|
+
end_datetime: str,
|
|
708
|
+
availability_view_interval: int = 30,
|
|
709
|
+
) -> dict[str, Any]:
|
|
710
|
+
"""
|
|
711
|
+
Retrieves free/busy information for a set of users, groups, or resources.
|
|
712
|
+
|
|
713
|
+
Args:
|
|
714
|
+
schedules (list[str]): A list of SMTP addresses (emails) to get schedules for.
|
|
715
|
+
start_datetime (str): The start of the time range (ISO 8601 with 'Z', e.g., '2023-12-25T00:00:00Z').
|
|
716
|
+
end_datetime (str): The end of the time range (ISO 8601 with 'Z', e.g., '2023-12-26T00:00:00Z').
|
|
717
|
+
availability_view_interval (int, optional): The duration of each time slot in minutes. Defaults to 30.
|
|
718
|
+
|
|
719
|
+
Returns:
|
|
720
|
+
dict[str, Any]: A dictionary containing schedule information.
|
|
721
|
+
|
|
722
|
+
Tags:
|
|
723
|
+
important
|
|
724
|
+
"""
|
|
725
|
+
user_id = await self._get_user_id()
|
|
726
|
+
url = f"{self.base_url}/users/{user_id}/calendar/getSchedule"
|
|
727
|
+
request_body = {
|
|
728
|
+
"schedules": schedules,
|
|
729
|
+
"startTime": {"dateTime": start_datetime, "timeZone": "UTC"},
|
|
730
|
+
"endTime": {"dateTime": end_datetime, "timeZone": "UTC"},
|
|
731
|
+
"availabilityViewInterval": availability_view_interval,
|
|
732
|
+
}
|
|
733
|
+
response = await self._apost(url, data=request_body, params={}, content_type="application/json")
|
|
734
|
+
return self._handle_response(response)
|
|
735
|
+
|
|
736
|
+
async def get_next_page_results(self, url: str) -> dict[str, Any]:
|
|
737
|
+
"""
|
|
738
|
+
Retrieves the next page of results from a paginated API response.
|
|
739
|
+
|
|
740
|
+
Args:
|
|
741
|
+
url (str): The full URL for the next page of results (@odata.nextLink).
|
|
742
|
+
|
|
743
|
+
Returns:
|
|
744
|
+
dict[str, Any]: A dictionary containing the next page of results.
|
|
745
|
+
|
|
746
|
+
Raises:
|
|
747
|
+
ValueError: If the URL is missing or invalid.
|
|
748
|
+
Tags:
|
|
749
|
+
important
|
|
417
750
|
"""
|
|
418
751
|
if not url:
|
|
419
752
|
raise ValueError("Missing required parameter 'url'.")
|
|
420
753
|
if not url.startswith(self.base_url):
|
|
421
|
-
raise ValueError(
|
|
422
|
-
f"The provided URL '{url}' does not start with the expected base URL '{self.base_url}'."
|
|
423
|
-
)
|
|
754
|
+
raise ValueError(f"The provided URL must start with '{self.base_url}'.")
|
|
424
755
|
relative_part = url[len(self.base_url) :]
|
|
425
756
|
parsed_relative = urlparse(relative_part)
|
|
426
757
|
path_only = parsed_relative.path
|
|
427
758
|
params = {k: v[0] for k, v in parse_qs(parsed_relative.query).items()}
|
|
428
|
-
response = self.
|
|
759
|
+
response = await self._aget(path_only, params=params)
|
|
429
760
|
return self._handle_response(response)
|
|
430
761
|
|
|
431
762
|
def list_tools(self):
|
|
432
763
|
return [
|
|
433
|
-
self.
|
|
434
|
-
self.
|
|
435
|
-
self.
|
|
436
|
-
self.
|
|
437
|
-
self.
|
|
438
|
-
self.
|
|
439
|
-
self.
|
|
440
|
-
self.
|
|
441
|
-
self.
|
|
764
|
+
self.reply_to_email,
|
|
765
|
+
self.send_email,
|
|
766
|
+
self.get_email_folder,
|
|
767
|
+
self.list_emails,
|
|
768
|
+
self.get_email,
|
|
769
|
+
self.delete_email,
|
|
770
|
+
self.list_email_attachments,
|
|
771
|
+
self.get_attachment,
|
|
772
|
+
self.get_my_profile,
|
|
773
|
+
self.get_next_page_results,
|
|
774
|
+
self.list_calendars,
|
|
775
|
+
self.get_calendar,
|
|
776
|
+
self.create_calendar,
|
|
777
|
+
self.update_calendar,
|
|
778
|
+
self.delete_calendar,
|
|
779
|
+
self.list_events,
|
|
780
|
+
self.get_event,
|
|
781
|
+
self.create_event,
|
|
782
|
+
self.update_event,
|
|
783
|
+
self.delete_event,
|
|
784
|
+
self.list_calendar_view,
|
|
785
|
+
self.get_schedule,
|
|
442
786
|
]
|