11from autohive_integrations_sdk import (
2- Integration , ExecutionContext , ActionHandler , ActionResult
2+ Integration ,
3+ ExecutionContext ,
4+ ActionHandler ,
5+ ActionResult ,
36)
47from typing import Dict , Any , List , Optional
58from datetime import datetime , timezone
@@ -20,12 +23,16 @@ async def fetch_json(context: ExecutionContext, url: str) -> Optional[Any]:
2023 return None
2124
2225
23- async def fetch_item (context : ExecutionContext , item_id : int ) -> Optional [Dict [str , Any ]]:
26+ async def fetch_item (
27+ context : ExecutionContext , item_id : int
28+ ) -> Optional [Dict [str , Any ]]:
2429 """Fetch a single item by ID."""
2530 return await fetch_json (context , f"{ BASE_URL } /item/{ item_id } .json" )
2631
2732
28- async def fetch_items_batch (context : ExecutionContext , item_ids : List [int ]) -> List [Dict [str , Any ]]:
33+ async def fetch_items_batch (
34+ context : ExecutionContext , item_ids : List [int ]
35+ ) -> List [Dict [str , Any ]]:
2936 """Fetch multiple items concurrently."""
3037 tasks = [fetch_item (context , item_id ) for item_id in item_ids ]
3138 results = await asyncio .gather (* tasks )
@@ -43,40 +50,42 @@ def format_item(item: Dict[str, Any]) -> Dict[str, Any]:
4350 "descendants" : item .get ("descendants" , 0 ),
4451 "hn_url" : f"{ HN_ITEM_URL } { item .get ('id' )} " ,
4552 }
46-
53+
4754 if item .get ("time" ):
4855 formatted ["time" ] = datetime .fromtimestamp (
4956 item ["time" ], tz = timezone .utc
5057 ).isoformat ()
51-
58+
5259 if item .get ("url" ):
5360 formatted ["url" ] = item ["url" ]
54-
61+
5562 if item .get ("text" ):
5663 formatted ["text" ] = item ["text" ]
57-
64+
5865 return formatted
5966
6067
61- def format_comment (item : Dict [str , Any ], replies : List [Dict [str , Any ]] = None ) -> Optional [Dict [str , Any ]]:
68+ def format_comment (
69+ item : Dict [str , Any ], replies : List [Dict [str , Any ]] = None
70+ ) -> Optional [Dict [str , Any ]]:
6271 """Format a comment for LLM-friendly output."""
6372 if item .get ("deleted" ) or item .get ("dead" ):
6473 return None
65-
74+
6675 formatted = {
6776 "id" : item .get ("id" ),
6877 "by" : item .get ("by" , "[deleted]" ),
6978 "text" : item .get ("text" , "" ),
7079 }
71-
80+
7281 if item .get ("time" ):
7382 formatted ["time" ] = datetime .fromtimestamp (
7483 item ["time" ], tz = timezone .utc
7584 ).isoformat ()
76-
85+
7786 if replies :
7887 formatted ["replies" ] = replies
79-
88+
8089 return formatted
8190
8291
@@ -85,240 +94,237 @@ async def fetch_comments_recursive(
8594 comment_ids : List [int ],
8695 limit : int ,
8796 current_depth : int ,
88- max_depth : int
97+ max_depth : int ,
8998) -> List [Dict [str , Any ]]:
9099 """Recursively fetch comments up to a certain depth."""
91100 if not comment_ids or current_depth > max_depth :
92101 return []
93-
102+
94103 limited_ids = comment_ids [:limit ] if current_depth == 1 else comment_ids [:10 ]
95104 comments = await fetch_items_batch (context , limited_ids )
96-
105+
97106 result = []
98107 for comment in comments :
99108 if comment .get ("deleted" ) or comment .get ("dead" ):
100109 continue
101-
110+
102111 replies = []
103112 if current_depth < max_depth and comment .get ("kids" ):
104113 replies = await fetch_comments_recursive (
105- context ,
106- comment ["kids" ],
107- limit ,
108- current_depth + 1 ,
109- max_depth
114+ context , comment ["kids" ], limit , current_depth + 1 , max_depth
110115 )
111-
116+
112117 formatted = format_comment (comment , replies if replies else None )
113118 if formatted :
114119 result .append (formatted )
115-
120+
116121 return result
117122
118123
119124async def fetch_stories_list (
120- context : ExecutionContext ,
121- endpoint : str ,
122- limit : int ,
123- output_key : str = "stories"
125+ context : ExecutionContext , endpoint : str , limit : int , output_key : str = "stories"
124126) -> Dict [str , Any ]:
125127 """Generic function to fetch a list of stories from an endpoint."""
126128 story_ids = await fetch_json (context , f"{ BASE_URL } /{ endpoint } .json" )
127-
129+
128130 if not story_ids :
129131 return {
130132 output_key : [],
131133 "fetched_at" : datetime .now (timezone .utc ).isoformat (),
132- "count" : 0
134+ "count" : 0 ,
133135 }
134-
135- limited_ids = story_ids [:min (limit , 100 )]
136+
137+ limited_ids = story_ids [: min (limit , 100 )]
136138 items = await fetch_items_batch (context , limited_ids )
137-
139+
138140 formatted_items = []
139141 for item in items :
140142 if item :
141143 formatted_items .append (format_item (item ))
142-
144+
143145 return {
144146 output_key : formatted_items ,
145147 "fetched_at" : datetime .now (timezone .utc ).isoformat (),
146- "count" : len (formatted_items )
148+ "count" : len (formatted_items ),
147149 }
148150
149151
150152@hackernews .action ("get_top_stories" )
151153class GetTopStoriesAction (ActionHandler ):
152154 """Fetch top stories from Hacker News."""
153155
154- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
156+ async def execute (
157+ self , inputs : Dict [str , Any ], context : ExecutionContext
158+ ) -> ActionResult :
155159 try :
156160 limit = inputs .get ("limit" , 30 )
157161 result = await fetch_stories_list (context , "topstories" , limit )
158162 return ActionResult (data = result , cost_usd = 0.0 )
159163 except Exception as e :
160164 return ActionResult (
161- data = {"stories" : [], "count" : 0 , "error" : str (e )},
162- cost_usd = 0.0
165+ data = {"stories" : [], "count" : 0 , "error" : str (e )}, cost_usd = 0.0
163166 )
164167
165168
166169@hackernews .action ("get_best_stories" )
167170class GetBestStoriesAction (ActionHandler ):
168171 """Fetch best stories from Hacker News."""
169172
170- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
173+ async def execute (
174+ self , inputs : Dict [str , Any ], context : ExecutionContext
175+ ) -> ActionResult :
171176 try :
172177 limit = inputs .get ("limit" , 30 )
173178 result = await fetch_stories_list (context , "beststories" , limit )
174179 return ActionResult (data = result , cost_usd = 0.0 )
175180 except Exception as e :
176181 return ActionResult (
177- data = {"stories" : [], "count" : 0 , "error" : str (e )},
178- cost_usd = 0.0
182+ data = {"stories" : [], "count" : 0 , "error" : str (e )}, cost_usd = 0.0
179183 )
180184
181185
182186@hackernews .action ("get_new_stories" )
183187class GetNewStoriesAction (ActionHandler ):
184188 """Fetch newest stories from Hacker News."""
185189
186- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
190+ async def execute (
191+ self , inputs : Dict [str , Any ], context : ExecutionContext
192+ ) -> ActionResult :
187193 try :
188194 limit = inputs .get ("limit" , 30 )
189195 result = await fetch_stories_list (context , "newstories" , limit )
190196 return ActionResult (data = result , cost_usd = 0.0 )
191197 except Exception as e :
192198 return ActionResult (
193- data = {"stories" : [], "count" : 0 , "error" : str (e )},
194- cost_usd = 0.0
199+ data = {"stories" : [], "count" : 0 , "error" : str (e )}, cost_usd = 0.0
195200 )
196201
197202
198203@hackernews .action ("get_ask_hn_stories" )
199204class GetAskHNStoriesAction (ActionHandler ):
200205 """Fetch Ask HN stories."""
201206
202- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
207+ async def execute (
208+ self , inputs : Dict [str , Any ], context : ExecutionContext
209+ ) -> ActionResult :
203210 try :
204211 limit = inputs .get ("limit" , 30 )
205212 result = await fetch_stories_list (context , "askstories" , limit )
206213 return ActionResult (data = result , cost_usd = 0.0 )
207214 except Exception as e :
208215 return ActionResult (
209- data = {"stories" : [], "count" : 0 , "error" : str (e )},
210- cost_usd = 0.0
216+ data = {"stories" : [], "count" : 0 , "error" : str (e )}, cost_usd = 0.0
211217 )
212218
213219
214220@hackernews .action ("get_show_hn_stories" )
215221class GetShowHNStoriesAction (ActionHandler ):
216222 """Fetch Show HN stories."""
217223
218- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
224+ async def execute (
225+ self , inputs : Dict [str , Any ], context : ExecutionContext
226+ ) -> ActionResult :
219227 try :
220228 limit = inputs .get ("limit" , 30 )
221229 result = await fetch_stories_list (context , "showstories" , limit )
222230 return ActionResult (data = result , cost_usd = 0.0 )
223231 except Exception as e :
224232 return ActionResult (
225- data = {"stories" : [], "count" : 0 , "error" : str (e )},
226- cost_usd = 0.0
233+ data = {"stories" : [], "count" : 0 , "error" : str (e )}, cost_usd = 0.0
227234 )
228235
229236
230237@hackernews .action ("get_job_stories" )
231238class GetJobStoriesAction (ActionHandler ):
232239 """Fetch job postings from Hacker News."""
233240
234- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
241+ async def execute (
242+ self , inputs : Dict [str , Any ], context : ExecutionContext
243+ ) -> ActionResult :
235244 try :
236245 limit = inputs .get ("limit" , 30 )
237- result = await fetch_stories_list (context , "jobstories" , limit , output_key = "jobs" )
246+ result = await fetch_stories_list (
247+ context , "jobstories" , limit , output_key = "jobs"
248+ )
238249 return ActionResult (data = result , cost_usd = 0.0 )
239250 except Exception as e :
240251 return ActionResult (
241- data = {"jobs" : [], "count" : 0 , "error" : str (e )},
242- cost_usd = 0.0
252+ data = {"jobs" : [], "count" : 0 , "error" : str (e )}, cost_usd = 0.0
243253 )
244254
245255
246256@hackernews .action ("get_story_with_comments" )
247257class GetStoryWithCommentsAction (ActionHandler ):
248258 """Fetch a story with its comments."""
249259
250- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
260+ async def execute (
261+ self , inputs : Dict [str , Any ], context : ExecutionContext
262+ ) -> ActionResult :
251263 try :
252264 story_id = inputs ["story_id" ]
253265 comment_limit = inputs .get ("comment_limit" , 20 )
254266 comment_depth = inputs .get ("comment_depth" , 2 )
255-
267+
256268 story = await fetch_item (context , story_id )
257-
269+
258270 if not story :
259271 return ActionResult (
260- data = {"error" : f"Story with ID { story_id } not found" },
261- cost_usd = 0.0
272+ data = {"error" : f"Story with ID { story_id } not found" }, cost_usd = 0.0
262273 )
263-
274+
264275 comments = []
265276 if story .get ("kids" ):
266277 comments = await fetch_comments_recursive (
267278 context ,
268279 story ["kids" ],
269280 comment_limit ,
270281 current_depth = 1 ,
271- max_depth = comment_depth
282+ max_depth = comment_depth ,
272283 )
273-
284+
274285 return ActionResult (
275286 data = {
276287 "story" : format_item (story ),
277288 "comments" : comments ,
278- "fetched_at" : datetime .now (timezone .utc ).isoformat ()
289+ "fetched_at" : datetime .now (timezone .utc ).isoformat (),
279290 },
280- cost_usd = 0.0
291+ cost_usd = 0.0 ,
281292 )
282293 except Exception as e :
283- return ActionResult (
284- data = {"error" : str (e )},
285- cost_usd = 0.0
286- )
294+ return ActionResult (data = {"error" : str (e )}, cost_usd = 0.0 )
287295
288296
289297@hackernews .action ("get_user_profile" )
290298class GetUserProfileAction (ActionHandler ):
291299 """Fetch a user's public profile."""
292300
293- async def execute (self , inputs : Dict [str , Any ], context : ExecutionContext ) -> ActionResult :
301+ async def execute (
302+ self , inputs : Dict [str , Any ], context : ExecutionContext
303+ ) -> ActionResult :
294304 try :
295305 username = inputs ["username" ]
296-
306+
297307 user = await fetch_json (context , f"{ BASE_URL } /user/{ username } .json" )
298-
308+
299309 if not user :
300310 return ActionResult (
301- data = {"error" : f"User '{ username } ' not found" },
302- cost_usd = 0.0
311+ data = {"error" : f"User '{ username } ' not found" }, cost_usd = 0.0
303312 )
304-
313+
305314 result = {
306315 "id" : user .get ("id" ),
307316 "karma" : user .get ("karma" , 0 ),
308- "profile_url" : f"{ HN_USER_URL } { username } "
317+ "profile_url" : f"{ HN_USER_URL } { username } " ,
309318 }
310-
319+
311320 if user .get ("created" ):
312321 result ["created" ] = datetime .fromtimestamp (
313322 user ["created" ], tz = timezone .utc
314323 ).isoformat ()
315-
324+
316325 if user .get ("about" ):
317326 result ["about" ] = user ["about" ]
318-
327+
319328 return ActionResult (data = result , cost_usd = 0.0 )
320329 except Exception as e :
321- return ActionResult (
322- data = {"error" : str (e )},
323- cost_usd = 0.0
324- )
330+ return ActionResult (data = {"error" : str (e )}, cost_usd = 0.0 )
0 commit comments