PostGen / backend /app /services /linkedin_service.py
Seth
update
f80e9b3
import httpx
import os
from typing import Dict, Any, Optional
from datetime import datetime
LINKEDIN_API_BASE = "https://api.linkedin.com/v2"
class LinkedInService:
def __init__(self, access_token: str):
self.access_token = access_token
self.headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"X-Restli-Protocol-Version": "2.0.0"
}
async def get_user_profile(self) -> Dict[str, Any]:
"""Get LinkedIn user profile"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"{LINKEDIN_API_BASE}/userinfo",
headers=self.headers,
timeout=30.0
)
response.raise_for_status()
return response.json()
async def create_post(self, text: str, media_uris: Optional[list] = None) -> Dict[str, Any]:
"""Create a LinkedIn post"""
# First, register upload if media is provided
media_urn = None
if media_uris:
# Register media upload
media_urn = await self._register_upload(media_uris[0])
# Create the post
post_data = {
"author": f"urn:li:person:{os.getenv('LINKEDIN_PERSON_URN', '')}",
"lifecycleState": "PUBLISHED",
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": {
"text": text
},
"shareMediaCategory": "IMAGE" if media_urn else "NONE"
}
},
"visibility": {
"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
}
}
if media_urn:
post_data["specificContent"]["com.linkedin.ugc.ShareContent"]["media"] = [
{
"status": "READY",
"media": media_urn
}
]
async with httpx.AsyncClient() as client:
response = await client.post(
f"{LINKEDIN_API_BASE}/ugcPosts",
headers=self.headers,
json=post_data,
timeout=30.0
)
response.raise_for_status()
return response.json()
async def _register_upload(self, image_url: str) -> str:
"""Register an image upload for LinkedIn"""
# This is a simplified version - actual implementation would upload the image
# and get a URN back
async with httpx.AsyncClient() as client:
# Register upload
register_response = await client.post(
f"{LINKEDIN_API_BASE}/assets?action=registerUpload",
headers=self.headers,
json={
"registerUploadRequest": {
"recipes": ["urn:li:digitalmediaRecipe:feedshare-image"],
"owner": f"urn:li:person:{os.getenv('LINKEDIN_PERSON_URN', '')}",
"serviceRelationships": [
{
"relationshipType": "OWNER",
"identifier": "urn:li:userGeneratedContent"
}
]
}
},
timeout=30.0
)
register_response.raise_for_status()
register_data = register_response.json()
# Upload the image
upload_url = register_data["value"]["uploadMechanism"]["com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"]["uploadUrl"]
asset_urn = register_data["value"]["asset"]
# Download and upload the image
image_response = await client.get(image_url, timeout=30.0)
image_response.raise_for_status()
await client.put(
upload_url,
content=image_response.content,
headers={"Content-Type": "application/octet-stream"},
timeout=30.0
)
return asset_urn