universal-mcp-applications 0.1.30rc1__py3-none-any.whl → 0.1.36rc1__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.

Files changed (106) hide show
  1. universal_mcp/applications/ahrefs/app.py +52 -198
  2. universal_mcp/applications/airtable/app.py +23 -122
  3. universal_mcp/applications/apollo/app.py +111 -464
  4. universal_mcp/applications/asana/app.py +417 -1567
  5. universal_mcp/applications/aws_s3/app.py +33 -100
  6. universal_mcp/applications/bill/app.py +546 -1957
  7. universal_mcp/applications/box/app.py +1068 -3981
  8. universal_mcp/applications/braze/app.py +364 -1430
  9. universal_mcp/applications/browser_use/app.py +2 -8
  10. universal_mcp/applications/cal_com_v2/app.py +207 -625
  11. universal_mcp/applications/calendly/app.py +61 -200
  12. universal_mcp/applications/canva/app.py +45 -110
  13. universal_mcp/applications/clickup/app.py +207 -674
  14. universal_mcp/applications/coda/app.py +146 -426
  15. universal_mcp/applications/confluence/app.py +310 -1098
  16. universal_mcp/applications/contentful/app.py +36 -151
  17. universal_mcp/applications/crustdata/app.py +28 -107
  18. universal_mcp/applications/dialpad/app.py +283 -756
  19. universal_mcp/applications/digitalocean/app.py +1766 -5777
  20. universal_mcp/applications/domain_checker/app.py +3 -54
  21. universal_mcp/applications/e2b/app.py +14 -64
  22. universal_mcp/applications/elevenlabs/app.py +9 -47
  23. universal_mcp/applications/exa/app.py +6 -17
  24. universal_mcp/applications/falai/app.py +23 -100
  25. universal_mcp/applications/figma/app.py +53 -137
  26. universal_mcp/applications/file_system/app.py +2 -13
  27. universal_mcp/applications/firecrawl/app.py +51 -152
  28. universal_mcp/applications/fireflies/app.py +59 -281
  29. universal_mcp/applications/fpl/app.py +91 -528
  30. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  31. universal_mcp/applications/fpl/utils/helper.py +25 -89
  32. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  33. universal_mcp/applications/ghost_content/app.py +52 -161
  34. universal_mcp/applications/github/app.py +19 -56
  35. universal_mcp/applications/gong/app.py +88 -248
  36. universal_mcp/applications/google_calendar/app.py +16 -68
  37. universal_mcp/applications/google_docs/app.py +88 -188
  38. universal_mcp/applications/google_drive/app.py +140 -462
  39. universal_mcp/applications/google_gemini/app.py +12 -64
  40. universal_mcp/applications/google_mail/app.py +28 -157
  41. universal_mcp/applications/google_searchconsole/app.py +15 -48
  42. universal_mcp/applications/google_sheet/app.py +101 -578
  43. universal_mcp/applications/google_sheet/helper.py +10 -37
  44. universal_mcp/applications/hashnode/app.py +57 -269
  45. universal_mcp/applications/heygen/app.py +44 -122
  46. universal_mcp/applications/http_tools/app.py +10 -32
  47. universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
  48. universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
  49. universal_mcp/applications/hubspot/app.py +23 -87
  50. universal_mcp/applications/jira/app.py +2071 -7986
  51. universal_mcp/applications/klaviyo/app.py +494 -1376
  52. universal_mcp/applications/linkedin/README.md +23 -4
  53. universal_mcp/applications/linkedin/app.py +392 -212
  54. universal_mcp/applications/mailchimp/app.py +450 -1605
  55. universal_mcp/applications/markitdown/app.py +8 -20
  56. universal_mcp/applications/miro/app.py +217 -699
  57. universal_mcp/applications/ms_teams/app.py +64 -186
  58. universal_mcp/applications/neon/app.py +86 -192
  59. universal_mcp/applications/notion/app.py +21 -36
  60. universal_mcp/applications/onedrive/app.py +14 -36
  61. universal_mcp/applications/openai/app.py +42 -165
  62. universal_mcp/applications/outlook/app.py +16 -76
  63. universal_mcp/applications/perplexity/app.py +4 -19
  64. universal_mcp/applications/pipedrive/app.py +832 -3142
  65. universal_mcp/applications/posthog/app.py +163 -432
  66. universal_mcp/applications/reddit/app.py +40 -139
  67. universal_mcp/applications/resend/app.py +41 -107
  68. universal_mcp/applications/retell/app.py +14 -41
  69. universal_mcp/applications/rocketlane/app.py +221 -934
  70. universal_mcp/applications/scraper/README.md +7 -4
  71. universal_mcp/applications/scraper/app.py +280 -93
  72. universal_mcp/applications/semanticscholar/app.py +22 -64
  73. universal_mcp/applications/semrush/app.py +43 -77
  74. universal_mcp/applications/sendgrid/app.py +512 -1262
  75. universal_mcp/applications/sentry/app.py +271 -906
  76. universal_mcp/applications/serpapi/app.py +40 -143
  77. universal_mcp/applications/sharepoint/app.py +15 -37
  78. universal_mcp/applications/shopify/app.py +1551 -4287
  79. universal_mcp/applications/shortcut/app.py +155 -417
  80. universal_mcp/applications/slack/app.py +50 -101
  81. universal_mcp/applications/spotify/app.py +126 -325
  82. universal_mcp/applications/supabase/app.py +104 -213
  83. universal_mcp/applications/tavily/app.py +1 -1
  84. universal_mcp/applications/trello/app.py +693 -2656
  85. universal_mcp/applications/twilio/app.py +14 -50
  86. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  87. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  88. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  89. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  90. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  91. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  92. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  93. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  94. universal_mcp/applications/whatsapp/app.py +35 -186
  95. universal_mcp/applications/whatsapp/audio.py +2 -6
  96. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  97. universal_mcp/applications/whatsapp_business/app.py +70 -283
  98. universal_mcp/applications/wrike/app.py +45 -118
  99. universal_mcp/applications/yahoo_finance/app.py +19 -65
  100. universal_mcp/applications/youtube/app.py +75 -261
  101. universal_mcp/applications/zenquotes/app.py +2 -2
  102. {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/METADATA +2 -2
  103. {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/RECORD +105 -106
  104. universal_mcp/applications/scraper/scraper_testers.py +0 -17
  105. {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/WHEEL +0 -0
  106. {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/licenses/LICENSE +0 -0
@@ -56,9 +56,7 @@ def analyze_sheet_for_tables(
56
56
  return tables
57
57
 
58
58
 
59
- def analyze_table_schema(
60
- get_values_func, spreadsheet_id: str, table_info: dict, sample_size: int = 50
61
- ) -> dict[str, Any]:
59
+ def analyze_table_schema(get_values_func, spreadsheet_id: str, table_info: dict, sample_size: int = 50) -> dict[str, Any]:
62
60
  """
63
61
  Analyze table structure and infer column names, types, and constraints.
64
62
 
@@ -115,9 +113,7 @@ def analyze_columns(sample_values: list[list[Any]]) -> list[dict]:
115
113
  columns = []
116
114
 
117
115
  for col_idx in range(len(headers)):
118
- column_name = (
119
- str(headers[col_idx]) if col_idx < len(headers) else f"Column_{col_idx + 1}"
120
- )
116
+ column_name = str(headers[col_idx]) if col_idx < len(headers) else f"Column_{col_idx + 1}"
121
117
 
122
118
  # Extract column values
123
119
  column_values = []
@@ -134,12 +130,8 @@ def analyze_columns(sample_values: list[list[Any]]) -> list[dict]:
134
130
  "type": column_type,
135
131
  "constraints": constraints,
136
132
  "sample_values": column_values[:5], # First 5 sample values
137
- "null_count": sum(
138
- 1 for val in column_values if not val or str(val).strip() == ""
139
- ),
140
- "unique_count": len(
141
- set(str(val) for val in column_values if val and str(val).strip())
142
- ),
133
+ "null_count": sum(1 for val in column_values if not val or str(val).strip() == ""),
134
+ "unique_count": len(set(str(val) for val in column_values if val and str(val).strip())),
143
135
  }
144
136
 
145
137
  columns.append(column_info)
@@ -159,11 +151,7 @@ def infer_column_type(values: list[Any]) -> tuple[str, dict]:
159
151
  return "TEXT", {}
160
152
 
161
153
  # Check for boolean values
162
- boolean_count = sum(
163
- 1
164
- for val in non_empty_values
165
- if str(val).lower() in ["true", "false", "yes", "no", "1", "0"]
166
- )
154
+ boolean_count = sum(1 for val in non_empty_values if str(val).lower() in ["true", "false", "yes", "no", "1", "0"])
167
155
  if boolean_count / len(non_empty_values) >= 0.8:
168
156
  return "BOOLEAN", {}
169
157
 
@@ -215,9 +203,7 @@ def infer_column_type(values: list[Any]) -> tuple[str, dict]:
215
203
  return "TEXT", {}
216
204
 
217
205
 
218
- def find_table_regions(
219
- values: list[list], min_rows: int, min_columns: int
220
- ) -> list[dict]:
206
+ def find_table_regions(values: list[list], min_rows: int, min_columns: int) -> list[dict]:
221
207
  """Find potential table regions in the data."""
222
208
  regions = []
223
209
 
@@ -290,9 +276,7 @@ def calculate_table_confidence(values: list[list], region: dict) -> float:
290
276
 
291
277
  # Calculate confidence based on data consistency
292
278
  total_cells = sum(len(row) for row in region_data)
293
- non_empty_cells = sum(
294
- sum(1 for cell in row if cell and str(cell).strip()) for row in region_data
295
- )
279
+ non_empty_cells = sum(sum(1 for cell in row if cell and str(cell).strip()) for row in region_data)
296
280
 
297
281
  if total_cells == 0:
298
282
  return 0.0
@@ -328,11 +312,7 @@ def has_header_row(data: list[list]) -> bool:
328
312
 
329
313
  # Check if header row has mostly text values
330
314
  header_text_count = sum(
331
- 1
332
- for cell in header_row
333
- if cell
334
- and isinstance(cell, str)
335
- and not cell.replace(".", "").replace("-", "").isdigit()
315
+ 1 for cell in header_row if cell and isinstance(cell, str) and not cell.replace(".", "").replace("-", "").isdigit()
336
316
  )
337
317
 
338
318
  # Check if data rows have different data types than header
@@ -358,18 +338,11 @@ def has_consistent_columns(data: list[list]) -> bool:
358
338
  column_values = [row[col] for row in data if col < len(row) and row[col]]
359
339
  if len(column_values) >= 2:
360
340
  # Check if column has consistent type
361
- numeric_count = sum(
362
- 1
363
- for val in column_values
364
- if str(val).replace(".", "").replace("-", "").isdigit()
365
- )
341
+ numeric_count = sum(1 for val in column_values if str(val).replace(".", "").replace("-", "").isdigit())
366
342
  text_count = len(column_values) - numeric_count
367
343
 
368
344
  # If 80% of values are same type, consider consistent
369
- if (
370
- numeric_count / len(column_values) >= 0.8
371
- or text_count / len(column_values) >= 0.8
372
- ):
345
+ if numeric_count / len(column_values) >= 0.8 or text_count / len(column_values) >= 0.8:
373
346
  consistent_columns += 1
374
347
 
375
348
  return consistent_columns / total_columns >= 0.6 if total_columns > 0 else False
@@ -8,7 +8,7 @@ class HashnodeApp(GraphQLApplication):
8
8
  super().__init__(name="hashnode", base_url="https://gql.hashnode.com", **kwargs)
9
9
  self.integration = integration
10
10
 
11
- def publish_post(
11
+ async def publish_post(
12
12
  self,
13
13
  publication_id: str,
14
14
  title: str,
@@ -38,42 +38,22 @@ class HashnodeApp(GraphQLApplication):
38
38
  Tags:
39
39
  publish, post, hashnode, api, important
40
40
  """
41
- publish_post_mutation = gql("""
42
- mutation PublishPost($input: PublishPostInput!) {
43
- publishPost(input: $input) {
44
- post {
45
- url
46
- }
47
- }
48
- }
49
- """)
50
-
51
- variables = {
52
- "input": {
53
- "publicationId": publication_id,
54
- "title": title,
55
- "contentMarkdown": content,
56
- }
57
- }
58
-
41
+ publish_post_mutation = gql(
42
+ "\n mutation PublishPost($input: PublishPostInput!) {\n publishPost(input: $input) {\n post {\n url\n }\n }\n }\n "
43
+ )
44
+ variables = {"input": {"publicationId": publication_id, "title": title, "contentMarkdown": content}}
59
45
  if tags:
60
- variables["input"]["tags"] = [
61
- {"name": tag, "slug": tag.replace(" ", "-").lower()} for tag in tags
62
- ]
63
-
46
+ variables["input"]["tags"] = [{"name": tag, "slug": tag.replace(" ", "-").lower()} for tag in tags]
64
47
  if slug:
65
48
  variables["input"]["slug"] = slug
66
-
67
49
  if subtitle:
68
50
  variables["input"]["subtitle"] = subtitle
69
-
70
51
  if cover_image:
71
52
  variables["input"]["coverImageOptions"] = {"coverImageURL": cover_image}
72
-
73
53
  result = self.mutate(publish_post_mutation, variables)
74
54
  return result["publishPost"]["post"]["url"]
75
55
 
76
- def create_draft(
56
+ async def create_draft(
77
57
  self,
78
58
  publication_id: str,
79
59
  title: str,
@@ -104,39 +84,22 @@ class HashnodeApp(GraphQLApplication):
104
84
  Tags:
105
85
  create, draft, post, hashnode, api, important
106
86
  """
107
- create_draft_mutation = gql("""
108
- mutation CreateDraft($input: CreateDraftInput!) {
109
- createDraft(input: $input) {
110
- draft {
111
- id
112
- slug
113
- title
114
- }
115
- }
116
- }
117
- """)
118
- variables = {
119
- "input": {
120
- "publicationId": publication_id,
121
- "title": title,
122
- "contentMarkdown": content,
123
- }
124
- }
87
+ create_draft_mutation = gql(
88
+ "\n mutation CreateDraft($input: CreateDraftInput!) {\n createDraft(input: $input) {\n draft {\n id\n slug\n title\n }\n }\n }\n "
89
+ )
90
+ variables = {"input": {"publicationId": publication_id, "title": title, "contentMarkdown": content}}
125
91
  if tags:
126
- variables["input"]["tags"] = [
127
- {"name": tag, "slug": tag.replace(" ", "-").lower()} for tag in tags
128
- ]
92
+ variables["input"]["tags"] = [{"name": tag, "slug": tag.replace(" ", "-").lower()} for tag in tags]
129
93
  if slug:
130
94
  variables["input"]["slug"] = slug
131
95
  if subtitle:
132
96
  variables["input"]["subtitle"] = subtitle
133
97
  if cover_image:
134
98
  variables["input"]["coverImageOptions"] = {"coverImageURL": cover_image}
135
-
136
99
  result = self.mutate(create_draft_mutation, variables)
137
100
  return result["createDraft"]["draft"]
138
101
 
139
- def publish_draft(self, post_id: str) -> str:
102
+ async def publish_draft(self, post_id: str) -> str:
140
103
  """
141
104
  Publishes a draft post.
142
105
 
@@ -152,20 +115,14 @@ class HashnodeApp(GraphQLApplication):
152
115
  Tags:
153
116
  publish, draft, post, hashnode, api, important
154
117
  """
155
- publish_draft_mutation = gql("""
156
- mutation PublishDraft($input: PublishDraftInput!) {
157
- publishDraft(input: $input) {
158
- post {
159
- url
160
- }
161
- }
162
- }
163
- """)
118
+ publish_draft_mutation = gql(
119
+ "\n mutation PublishDraft($input: PublishDraftInput!) {\n publishDraft(input: $input) {\n post {\n url\n }\n }\n }\n "
120
+ )
164
121
  variables = {"input": {"draftId": post_id}}
165
122
  result = self.mutate(publish_draft_mutation, variables)
166
123
  return result["publishDraft"]["post"]["url"]
167
124
 
168
- def get_me(self) -> dict:
125
+ async def get_me(self) -> dict:
169
126
  """
170
127
  Fetches details about the authenticated user.
171
128
 
@@ -178,58 +135,16 @@ class HashnodeApp(GraphQLApplication):
178
135
  Tags:
179
136
  get, me, hashnode, api, query, important
180
137
  """
181
- get_me_query = gql("""
182
- query Me {
183
- me {
184
- id
185
- username
186
- name
187
- bio {
188
- markdown
189
- html
190
- text
191
- }
192
- profilePicture
193
- socialMediaLinks {
194
- website
195
- github
196
- twitter
197
- instagram
198
- facebook
199
- stackoverflow
200
- linkedin
201
- youtube
202
- bluesky
203
- }
204
- emailNotificationPreferences {
205
- weeklyNewsletterEmails
206
- activityNotifications
207
- generalAnnouncements
208
- monthlyBlogStats
209
- newFollowersWeekly
210
- }
211
- followersCount
212
- followingsCount
213
- tagline
214
- dateJoined
215
- location
216
- availableFor
217
- email
218
- unverifiedEmail
219
- role
220
- }
221
- }
222
- """)
223
-
138
+ get_me_query = gql(
139
+ "\n query Me {\n me {\n id\n username\n name\n bio {\n markdown\n html\n text\n }\n profilePicture\n socialMediaLinks {\n website\n github\n twitter\n instagram\n facebook\n stackoverflow\n linkedin\n youtube\n bluesky\n }\n emailNotificationPreferences {\n weeklyNewsletterEmails\n activityNotifications\n generalAnnouncements\n monthlyBlogStats\n newFollowersWeekly\n }\n followersCount\n followingsCount\n tagline\n dateJoined\n location\n availableFor\n email\n unverifiedEmail\n role\n }\n }\n "
140
+ )
224
141
  result = self.query(get_me_query)
225
-
226
- # It's good practice to check if 'me' exists in the result
227
142
  if "me" in result:
228
143
  return result.get("me")
229
144
  else:
230
145
  raise Exception("Failed to retrieve 'me' data from Hashnode API response.")
231
146
 
232
- def get_publication(self, host: str = None, publication_id: str = None) -> dict:
147
+ async def get_publication(self, host: str = None, publication_id: str = None) -> dict:
233
148
  """
234
149
  Fetches details about a publication by host or ID. Only one of host or publication_id should be provided.
235
150
 
@@ -247,40 +162,18 @@ class HashnodeApp(GraphQLApplication):
247
162
  Tags:
248
163
  get, publication, hashnode, api, query, important
249
164
  """
250
- if not host and not publication_id:
165
+ if not host and (not publication_id):
251
166
  raise ValueError("Either host or publication_id must be provided.")
252
-
253
- query_string = """
254
- query Publication($host: String, $id: ObjectId) {
255
- publication(host: $host, id: $id) {
256
- id
257
- title
258
- url
259
- isTeam
260
- posts(first: 5) {
261
- edges {
262
- node {
263
- id
264
- }
265
- }
266
- }
267
- }
268
- }
269
- """
167
+ query_string = "\n query Publication($host: String, $id: ObjectId) {\n publication(host: $host, id: $id) {\n id\n title\n url\n isTeam\n posts(first: 5) {\n edges {\n node {\n id\n }\n }\n }\n }\n }\n "
270
168
  get_publication_query = gql(query_string)
271
-
272
- # Construct variables such that only one of host or id is included
273
169
  if host:
274
170
  variables = {"host": host}
275
171
  else:
276
172
  variables = {"id": publication_id}
277
-
278
173
  result = self.query(get_publication_query, variables)
279
174
  return result.get("publication")
280
175
 
281
- def list_posts(
282
- self, publication_id: str, first: int = 10, after: str = None
283
- ) -> dict:
176
+ async def list_posts(self, publication_id: str, first: int = 10, after: str = None) -> dict:
284
177
  """
285
178
  Lists posts from a publication.
286
179
 
@@ -298,36 +191,16 @@ class HashnodeApp(GraphQLApplication):
298
191
  Tags:
299
192
  list, posts, hashnode, api, query, important
300
193
  """
301
- list_posts_query = gql("""
302
- query Publication($id: ObjectId!, $first: Int!, $after: String) {
303
- publication(id: $id) {
304
- posts(first: $first, after: $after) {
305
- edges {
306
- node {
307
- id
308
- title
309
- slug
310
- url
311
- }
312
- cursor
313
- }
314
- pageInfo {
315
- endCursor
316
- hasNextPage
317
- }
318
- }
319
- }
320
- }
321
- """)
194
+ list_posts_query = gql(
195
+ "\n query Publication($id: ObjectId!, $first: Int!, $after: String) {\n publication(id: $id) {\n posts(first: $first, after: $after) {\n edges {\n node {\n id\n title\n slug\n url\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n "
196
+ )
322
197
  variables = {"id": publication_id, "first": first}
323
198
  if after:
324
199
  variables["after"] = after
325
200
  result = self.query(list_posts_query, variables)
326
201
  return result.get("publication", {}).get("posts")
327
202
 
328
- def get_post(
329
- self, post_id: str = None, slug: str = None, hostname: str = None
330
- ) -> dict:
203
+ async def get_post(self, post_id: str = None, slug: str = None, hostname: str = None) -> dict:
331
204
  """
332
205
  Fetches details of a single post by ID, or by slug and hostname.
333
206
 
@@ -346,45 +219,21 @@ class HashnodeApp(GraphQLApplication):
346
219
  Tags:
347
220
  get, post, hashnode, api, query, important
348
221
  """
349
- if not post_id and not (slug and hostname):
350
- raise ValueError(
351
- "Either post_id or both slug and hostname must be provided."
352
- )
353
-
354
- get_post_query = gql("""
355
- query Post($id: ID, $slug: String, $hostname: String) {
356
- post(id: $id, slug: $slug, hostname: $hostname) {
357
- id
358
- slug
359
- previousSlugs
360
- title
361
- subtitle
362
- author {
363
- id
364
- username
365
- name
366
- }
367
- comments(first: 5) {
368
- edges {
369
- node {
370
- id
371
- }
372
- }
373
- }
374
- }
375
- }
376
- """)
222
+ if not post_id and (not (slug and hostname)):
223
+ raise ValueError("Either post_id or both slug and hostname must be provided.")
224
+ get_post_query = gql(
225
+ "\n query Post($id: ID, $slug: String, $hostname: String) {\n post(id: $id, slug: $slug, hostname: $hostname) {\n id\n slug\n previousSlugs\n title\n subtitle\n \t\t\tauthor {\n id\n username\n name\n }\n comments(first: 5) {\n edges {\n node {\n id\n }\n }\n }\n }\n }\n "
226
+ )
377
227
  variables = {}
378
228
  if post_id:
379
229
  variables["id"] = post_id
380
230
  else:
381
231
  variables["slug"] = slug
382
232
  variables["hostname"] = hostname
383
-
384
233
  result = self.query(get_post_query, variables)
385
234
  return result.get("post")
386
235
 
387
- def modify_post(
236
+ async def modify_post(
388
237
  self,
389
238
  post_id: str,
390
239
  title: str = None,
@@ -415,41 +264,26 @@ class HashnodeApp(GraphQLApplication):
415
264
  Tags:
416
265
  modify, post, hashnode, api, important
417
266
  """
418
- update_post_mutation = gql("""
419
- mutation UpdatePost($input: UpdatePostInput!) {
420
- updatePost(input: $input) {
421
- post {
422
- url
423
- }
424
- }
425
- }
426
- """)
427
-
428
- variables = {
429
- "input": {
430
- "id": post_id,
431
- }
432
- }
433
-
267
+ update_post_mutation = gql(
268
+ "\n mutation UpdatePost($input: UpdatePostInput!) {\n updatePost(input: $input) {\n post {\n url\n }\n }\n }\n "
269
+ )
270
+ variables = {"input": {"id": post_id}}
434
271
  if title:
435
272
  variables["input"]["title"] = title
436
273
  if content:
437
274
  variables["input"]["contentMarkdown"] = content
438
275
  if tags:
439
- variables["input"]["tags"] = [
440
- {"name": tag, "slug": tag.replace(" ", "-").lower()} for tag in tags
441
- ]
276
+ variables["input"]["tags"] = [{"name": tag, "slug": tag.replace(" ", "-").lower()} for tag in tags]
442
277
  if slug:
443
278
  variables["input"]["slug"] = slug
444
279
  if subtitle:
445
280
  variables["input"]["subtitle"] = subtitle
446
281
  if cover_image:
447
282
  variables["input"]["coverImageOptions"] = {"coverImageURL": cover_image}
448
-
449
283
  result = self.mutate(update_post_mutation, variables)
450
284
  return result["updatePost"]["post"]["url"]
451
285
 
452
- def delete_post(self, post_id: str) -> str:
286
+ async def delete_post(self, post_id: str) -> str:
453
287
  """
454
288
  Deletes a post using the GraphQL API.
455
289
 
@@ -465,24 +299,14 @@ class HashnodeApp(GraphQLApplication):
465
299
  Tags:
466
300
  delete, post, hashnode, api
467
301
  """
468
- delete_post_mutation = gql("""
469
- mutation RemovePost($input: RemovePostInput!) {
470
- removePost(input: $input) {
471
- post {
472
- id
473
- slug
474
- previousSlugs
475
- title
476
- subtitle
477
- }
478
- }
479
- }
480
- """)
302
+ delete_post_mutation = gql(
303
+ "\n mutation RemovePost($input: RemovePostInput!) {\n removePost(input: $input) {\n post {\n id\n slug\n previousSlugs\n title\n subtitle\n }\n }\n }\n "
304
+ )
481
305
  variables = {"input": {"id": post_id}}
482
306
  result = self.mutate(delete_post_mutation, variables)
483
307
  return result.get("removePost", {}).get("post", "Post deleted successfully")
484
308
 
485
- def add_comment(self, post_id: str, content: str) -> dict:
309
+ async def add_comment(self, post_id: str, content: str) -> dict:
486
310
  """
487
311
  Adds a comment to a post using the GraphQL API.
488
312
 
@@ -499,32 +323,14 @@ class HashnodeApp(GraphQLApplication):
499
323
  Tags:
500
324
  add, comment, post, hashnode, api, important
501
325
  """
502
- add_comment_mutation = gql("""
503
- mutation AddComment($input: AddCommentInput!) {
504
- addComment(input: $input) {
505
- comment {
506
- id
507
- content {
508
- markdown
509
- }
510
- author {
511
- id
512
- username
513
- }
514
- }
515
- }
516
- }
517
- """)
518
- variables = {
519
- "input": {
520
- "postId": post_id,
521
- "contentMarkdown": content,
522
- }
523
- }
326
+ add_comment_mutation = gql(
327
+ "\n mutation AddComment($input: AddCommentInput!) {\n addComment(input: $input) {\n comment {\n id\n content {\n markdown\n }\n author {\n id\n username\n }\n }\n }\n }\n "
328
+ )
329
+ variables = {"input": {"postId": post_id, "contentMarkdown": content}}
524
330
  result = self.mutate(add_comment_mutation, variables)
525
331
  return result.get("addComment", {}).get("comment")
526
332
 
527
- def delete_comment(self, comment_id: str) -> str:
333
+ async def delete_comment(self, comment_id: str) -> str:
528
334
  """
529
335
  Deletes a comment using the GraphQL API.
530
336
 
@@ -540,22 +346,14 @@ class HashnodeApp(GraphQLApplication):
540
346
  Tags:
541
347
  delete, comment, hashnode, api
542
348
  """
543
- delete_comment_mutation = gql("""
544
- mutation RemoveComment($input: RemoveCommentInput!) {
545
- removeComment(input: $input) {
546
- comment {
547
- id
548
- }
549
- }
550
- }
551
- """)
349
+ delete_comment_mutation = gql(
350
+ "\n mutation RemoveComment($input: RemoveCommentInput!) {\n removeComment(input: $input) {\n comment {\n id\n }\n }\n }\n "
351
+ )
552
352
  variables = {"input": {"id": comment_id}}
553
353
  result = self.mutate(delete_comment_mutation, variables)
554
- return result.get("removeComment", {}).get(
555
- "comment", "Comment deleted successfully"
556
- )
354
+ return result.get("removeComment", {}).get("comment", "Comment deleted successfully")
557
355
 
558
- def get_user(self, username: str) -> dict:
356
+ async def get_user(self, username: str) -> dict:
559
357
  """
560
358
  Fetches details about a user by username.
561
359
 
@@ -571,19 +369,9 @@ class HashnodeApp(GraphQLApplication):
571
369
  Tags:
572
370
  get, user, hashnode, api, query
573
371
  """
574
- get_user_query = gql("""
575
- query User($username: String!) {
576
- user(username: $username) {
577
- id
578
- username
579
- name
580
- tagline
581
- profilePicture
582
- followersCount
583
- followingsCount
584
- }
585
- }
586
- """)
372
+ get_user_query = gql(
373
+ "\n query User($username: String!) {\n user(username: $username) {\n id\n username\n name\n tagline\n profilePicture\n followersCount\n followingsCount\n }\n }\n "
374
+ )
587
375
  variables = {"username": username}
588
376
  result = self.query(get_user_query, variables)
589
377
  return result.get("user")