206 lines
6.4 KiB
Python
206 lines
6.4 KiB
Python
import logging
|
|
import os
|
|
import sys
|
|
|
|
import requests
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
handlers=[logging.StreamHandler()],
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FolkAPI:
|
|
BASE_URL = "https://api.folk.app/v1"
|
|
|
|
def __init__(self, api_key: str):
|
|
api_key = os.environ.get("FOLK_API_KEY", api_key)
|
|
self.headers = {"Authorization": f"Bearer {api_key}"}
|
|
logger.info(f"FolkAPI initialized with API key: {api_key[:4]}***")
|
|
|
|
def get_groups(self):
|
|
"""Fetch all groups from Folk."""
|
|
url = f"{self.BASE_URL}/groups"
|
|
response = requests.get(url, headers=self.headers)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
def create_company(
|
|
self,
|
|
name: str,
|
|
group_id: str = None,
|
|
website: str = None,
|
|
linkedin_url: str = None,
|
|
description: str = None,
|
|
emails=None,
|
|
phones=None,
|
|
addresses=None,
|
|
urls=None,
|
|
custom_field_values=None,
|
|
groups=None,
|
|
**kwargs,
|
|
):
|
|
"""Create a company (investor) in a specific group.
|
|
|
|
This method builds a payload matching Folk's Create Company API:
|
|
https://developer.folk.app/api-reference/companies/create-a-company
|
|
|
|
It keeps backward compatibility with the previous `group_id`,
|
|
`website` and `linkedin_url` arguments.
|
|
"""
|
|
url = f"{self.BASE_URL}/companies"
|
|
|
|
# Build the top-level payload expected by Folk
|
|
data = {"name": name}
|
|
if description:
|
|
data["description"] = description
|
|
|
|
# Groups: prefer explicit `groups`, else fall back to `group_id`
|
|
if groups:
|
|
# Accept either list of ids or list of dicts
|
|
formatted = []
|
|
for g in groups:
|
|
if isinstance(g, dict) and g.get("id"):
|
|
formatted.append({"id": g["id"]})
|
|
else:
|
|
formatted.append({"id": str(g)})
|
|
data["groups"] = formatted
|
|
elif group_id:
|
|
data["groups"] = [{"id": group_id}]
|
|
|
|
# Helper to normalize single or multiple inputs into lists
|
|
def _to_list(val):
|
|
if val is None:
|
|
return None
|
|
if isinstance(val, (list, tuple)):
|
|
return [v for v in val if v is not None]
|
|
return [val]
|
|
|
|
# URLs: include website and linkedin_url if provided and merge with urls
|
|
urls_list = _to_list(urls) or []
|
|
if website:
|
|
urls_list.append(website)
|
|
if linkedin_url:
|
|
urls_list.append(linkedin_url)
|
|
if urls_list:
|
|
data["urls"] = urls_list
|
|
|
|
# Emails/phones/addresses
|
|
emails_list = _to_list(emails)
|
|
if emails_list:
|
|
data["emails"] = emails_list
|
|
phones_list = _to_list(phones)
|
|
if phones_list:
|
|
data["phones"] = phones_list
|
|
addresses_list = _to_list(addresses)
|
|
if addresses_list:
|
|
data["addresses"] = addresses_list
|
|
|
|
# Custom field values follow the API's structure
|
|
if custom_field_values:
|
|
data["customFieldValues"] = custom_field_values
|
|
|
|
# Allow passing any additional top-level fields via kwargs (careful)
|
|
for k, v in kwargs.items():
|
|
# don't overwrite keys we explicitly set
|
|
if k not in data:
|
|
data[k] = v
|
|
|
|
response = requests.post(url, headers=self.headers, json=data)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
def create_person(
|
|
self,
|
|
first_name: str,
|
|
last_name: str,
|
|
email: str = None,
|
|
company_id: str = None,
|
|
group_id: str = None,
|
|
linkedin_url: str = None,
|
|
companies=None,
|
|
emails=None,
|
|
phones=None,
|
|
addresses=None,
|
|
urls=None,
|
|
custom_field_values=None,
|
|
groups=None,
|
|
**kwargs,
|
|
):
|
|
"""Create a person in the workspace.
|
|
|
|
Builds payload matching Folk's Create Person API: use camelCase
|
|
keys (firstName, lastName, groups, companies, emails, etc.).
|
|
Keeps backward compatibility with `company_id` and `group_id`.
|
|
"""
|
|
url = f"{self.BASE_URL}/people"
|
|
|
|
data = {"firstName": first_name, "lastName": last_name}
|
|
|
|
# Groups: explicit `groups` preferred, else fallback to `group_id`
|
|
if groups:
|
|
formatted = []
|
|
for g in groups:
|
|
if isinstance(g, dict) and g.get("id"):
|
|
formatted.append({"id": g["id"]})
|
|
else:
|
|
formatted.append({"id": str(g)})
|
|
data["groups"] = formatted
|
|
elif group_id:
|
|
data["groups"] = [{"id": group_id}]
|
|
|
|
# Companies: keep backward compatibility with company_id
|
|
if companies:
|
|
formatted = []
|
|
for c in companies:
|
|
if isinstance(c, dict):
|
|
formatted.append(c)
|
|
elif isinstance(c, str):
|
|
# treat as id
|
|
formatted.append({"id": c})
|
|
if formatted:
|
|
data["companies"] = formatted
|
|
elif company_id:
|
|
data["companies"] = [{"id": company_id}]
|
|
|
|
# Helper to normalize into lists
|
|
def _to_list(val):
|
|
if val is None:
|
|
return None
|
|
if isinstance(val, (list, tuple)):
|
|
return [v for v in val if v is not None]
|
|
return [val]
|
|
|
|
emails_list = _to_list(emails) or []
|
|
if email:
|
|
emails_list.insert(0, email)
|
|
if emails_list:
|
|
data["emails"] = emails_list
|
|
|
|
phones_list = _to_list(phones)
|
|
if phones_list:
|
|
data["phones"] = phones_list
|
|
addresses_list = _to_list(addresses)
|
|
if addresses_list:
|
|
data["addresses"] = addresses_list
|
|
urls_list = _to_list(urls) or []
|
|
if linkedin_url:
|
|
urls_list.append(linkedin_url)
|
|
if urls_list:
|
|
data["urls"] = urls_list
|
|
|
|
if custom_field_values:
|
|
data["customFieldValues"] = custom_field_values
|
|
|
|
# Allow passthrough of other top-level fields in kwargs
|
|
for k, v in kwargs.items():
|
|
if k not in data:
|
|
data[k] = v
|
|
|
|
response = requests.post(url, headers=self.headers, json=data)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|