import os, requests, time
LI = os.environ["LINKEDIN_ACCESS_TOKEN"]
MV = os.environ["MAVERA_API_KEY"]
LI_BASE = "https://api.linkedin.com/rest"
MV_BASE = "https://app.mavera.io/api/v1"
LI_H = {"Authorization": f"Bearer {LI}", "LinkedIn-Version": "202401", "X-Restli-Protocol-Version": "2.0.0"}
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
ORG_URN = "urn:li:organization:12345678"
# 1. Pull recent posts and find high-engagement ones
r = requests.get(f"{LI_BASE}/posts",
headers=LI_H,
params={"q": "author", "author": ORG_URN, "count": 50, "sortBy": "LAST_MODIFIED"})
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 60)))
r = requests.get(f"{LI_BASE}/posts", headers=LI_H,
params={"q": "author", "author": ORG_URN, "count": 50, "sortBy": "LAST_MODIFIED"})
r.raise_for_status()
posts = r.json().get("elements", [])
# 2. Get comment counts to find top posts
post_comments = []
for post in posts:
post_urn = post.get("id", "")
commentary = post.get("commentary", "")
if not commentary:
continue
sr = requests.get(f"{LI_BASE}/socialActions/{post_urn}", headers=LI_H)
if sr.status_code == 429:
time.sleep(int(sr.headers.get("Retry-After", 30)))
sr = requests.get(f"{LI_BASE}/socialActions/{post_urn}", headers=LI_H)
comment_count = 0
if sr.ok:
comment_count = sr.json().get("commentsSummary", {}).get("totalFirstLevelComments", 0)
post_comments.append({
"urn": post_urn,
"commentary": commentary[:300],
"comment_count": comment_count,
})
time.sleep(0.3)
# 3. Pull actual comments from top posts (by comment count)
top_posts = sorted(post_comments, key=lambda x: -x["comment_count"])[:8]
all_comments = []
for post in top_posts:
if post["comment_count"] == 0:
continue
cr = requests.get(f"{LI_BASE}/socialActions/{post['urn']}/comments",
headers=LI_H,
params={"count": 100})
if cr.status_code == 429:
time.sleep(int(cr.headers.get("Retry-After", 30)))
cr = requests.get(f"{LI_BASE}/socialActions/{post['urn']}/comments",
headers=LI_H, params={"count": 100})
if cr.ok:
comments = cr.json().get("elements", [])
for c in comments:
text = c.get("message", {}).get("text", "").strip()
if text and len(text) > 15:
all_comments.append({
"post_context": post["commentary"][:150],
"comment": text[:400],
})
time.sleep(0.5)
if not all_comments:
raise SystemExit("No comments found. Check page permissions and post engagement.")
# 4. Build comment corpus for Mave
corpus = "\n\n---\n\n".join(
f"[Post context: {c['post_context'][:100]}...]\nComment: {c['comment']}"
for c in all_comments[:60]
)
# 5. Messaging analysis via Mave
analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
"message": f"""Analyze these {len(all_comments)} LinkedIn comments from our Company Page posts.
These are real prospects and followers reacting to our content.
{corpus}
Produce a messaging intelligence report:
1. QUESTIONS PROSPECTS ASK: What are the top 5-7 recurring questions? Group by theme (pricing, implementation, comparison, capability). Include exact phrasing from comments.
2. OBJECTIONS THAT SURFACE: What resistance or skepticism appears? List the top 5 objections with representative quotes and suggested counter-messaging.
3. LANGUAGE MAP: What words and phrases do commenters use to describe their problems? List the top 10 prospect-native phrases (not our internal jargon). These should be used verbatim in ad copy and landing pages.
4. SENTIMENT CLUSTERS: Group comments by sentiment (enthusiastic, curious, skeptical, critical). What percentage falls in each?
5. CONTENT GAPS: What topics do commenters ask about that we haven't addressed? List 5 content pieces we should create based on comment demand.
6. COMPETITIVE MENTIONS: Any competitors or alternatives mentioned? Context for each."""
}).json()
print(f"Analyzed {len(all_comments)} comments from {len(top_posts)} high-engagement posts")
print("=" * 60)
print(analysis.get("content", "")[:2500])
startparameter. High-comment posts are rare on Company Pages.