Skip to content

Commit c3a4748

Browse files
authored
fix: Fix LinkedIn get_user() and create_post() method. (#397)
* add a retry to get_user() method to fix bug. * craft the link to newly created post
1 parent 4c54d9e commit c3a4748

File tree

4 files changed

+44
-59
lines changed

4 files changed

+44
-59
lines changed

linkedin/main.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
from linkedin_api.clients.restli.client import RestliClient
2-
from tools import (
3-
users,
4-
posts,
5-
) # pylint: disable=unused-import. These are used in the tool registry.
6-
from tools.helper import tool_registry
2+
from tools.users import get_user
3+
from tools.posts import create_post
74
import json
85
import sys
96
import asyncio
@@ -17,7 +14,14 @@ async def main():
1714
command = sys.argv[1]
1815
client = RestliClient()
1916

20-
response = await tool_registry.get(command)(client)
17+
match command:
18+
case "GetCurrentUser":
19+
response = get_user(client)
20+
case "CreatePost":
21+
response = await create_post(client)
22+
case _:
23+
print(f"Unknown command: {command}")
24+
sys.exit(1)
2125
print(json.dumps(response, indent=4))
2226

2327

linkedin/tools/helper.py

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,50 +11,6 @@ def str_to_bool(value):
1111
return str(value).lower() in ("true", "1", "yes")
1212

1313

14-
class ToolRegistry:
15-
def __init__(self):
16-
self._tools = {}
17-
18-
def _register(self, name, func):
19-
"""
20-
Registers a tool by the given 'name'.
21-
Raises a ValueError if a tool with the same name is already registered.
22-
"""
23-
if name in self._tools:
24-
raise ValueError(f"Tool '{name}' is already registered.")
25-
self._tools[name] = func
26-
27-
def get(self, name):
28-
"""
29-
Retrieves a registered tool by name.
30-
Raises a ValueError if the tool is not found.
31-
"""
32-
if name not in self._tools:
33-
raise ValueError(f"Tool '{name}' not found.")
34-
return self._tools[name]
35-
36-
def list_tools(self):
37-
"""
38-
Returns a list of all registered tool names.
39-
"""
40-
return list(self._tools.keys())
41-
42-
def register_tool(self, name):
43-
"""
44-
A decorator that automatically registers the decorated function
45-
under the specified 'name' in the ToolRegistry.
46-
"""
47-
48-
def wrapper(func):
49-
self._register(name, func)
50-
return func
51-
52-
return wrapper
53-
54-
55-
tool_registry = ToolRegistry()
56-
57-
5814
def _prepend_base_path(base_path: str, file_path: str):
5915
"""
6016
Prepend a base path to a file path if it's not already rooted in the base path.

linkedin/tools/posts.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
from tools.helper import tool_registry, ACCESS_TOKEN, load_from_gptscript_workspace
1+
from tools.helper import ACCESS_TOKEN, load_from_gptscript_workspace
22
from linkedin_api.clients.restli.client import RestliClient
33
from tools.users import get_user
44
import os
55
import requests
66

77

8-
@tool_registry.register_tool("CreatePost")
9-
async def create_post(client: RestliClient):
8+
async def create_post(client: RestliClient) -> dict:
109
r = get_user(client)
1110
user_id = r["sub"]
1211
content = os.getenv("CONTENT")
@@ -97,10 +96,14 @@ async def create_post(client: RestliClient):
9796
raise ValueError(
9897
f"Error: failed to create post. Status code: {response.status_code}. Response: {response.entity}"
9998
)
99+
100+
response.entity["link"] = (
101+
f"https://www.linkedin.com/feed/update/{response.entity['id']}" # craft the link manually
102+
)
100103
return response.entity
101104

102105

103-
def _register_upload(client: RestliClient, category: str):
106+
def _register_upload(client: RestliClient, category: str) -> tuple[str, dict]:
104107
url = "https://api.linkedin.com/v2/assets?action=registerUpload"
105108

106109
headers = {
@@ -145,7 +148,7 @@ def _register_upload(client: RestliClient, category: str):
145148
)
146149

147150

148-
async def _upload_registered_file(file_path, upload_url):
151+
async def _upload_registered_file(file_path, upload_url) -> int:
149152
headers = {
150153
"Authorization": f"Bearer {ACCESS_TOKEN}",
151154
"Content-Type": "application/octet-stream",

linkedin/tools/users.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1-
from tools.helper import tool_registry, ACCESS_TOKEN
1+
from tools.helper import ACCESS_TOKEN
22
from linkedin_api.clients.restli.client import RestliClient
3+
import time
4+
import sys
35

46

5-
@tool_registry.register_tool("GetCurrentUser")
6-
def get_user(client: RestliClient):
7-
response = client.get(resource_path="/userinfo", access_token=ACCESS_TOKEN)
7+
def get_user(client: RestliClient) -> dict:
8+
9+
MAX_RETRIES = 3
10+
DELAY = 1 # seconds between retries
11+
12+
for attempt in range(1, MAX_RETRIES + 1):
13+
try:
14+
response = client.get(resource_path="/userinfo", access_token=ACCESS_TOKEN)
15+
if response.status_code >= 200 and response.status_code < 300:
16+
break
17+
except Exception as e:
18+
print(f"Attempt {attempt}: Failed with error: {e}", file=sys.stderr)
19+
print(
20+
f"Response: {response.status_code} - {response.entity}", file=sys.stderr
21+
)
22+
23+
if attempt < MAX_RETRIES:
24+
time.sleep(DELAY) # Wait before retrying
25+
else:
26+
raise Exception(
27+
f"Failed to get user info, Error: {response.status_code} - {response.entity}"
28+
)
29+
830
return response.entity

0 commit comments

Comments
 (0)