getsocialapi-facebook 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ from .client import Client
2
+ from .exceptions import (
3
+ APIKeyError,
4
+ BadRequestError,
5
+ GetSocialFacebookError,
6
+ RateLimitError,
7
+ ServiceUnavailableError,
8
+ UpstreamError,
9
+ )
10
+ from .facebook import FacebookAPI
11
+
12
+ __version__ = "0.1.0"
13
+
14
+ __all__ = [
15
+ "Client",
16
+ "FacebookAPI",
17
+ "GetSocialFacebookError",
18
+ "BadRequestError",
19
+ "APIKeyError",
20
+ "RateLimitError",
21
+ "UpstreamError",
22
+ "ServiceUnavailableError",
23
+ ]
@@ -0,0 +1,28 @@
1
+ import os
2
+
3
+ from .facebook import FacebookAPI
4
+ from .http import DEFAULT_BASE_URL, Transport
5
+
6
+
7
+ class Client:
8
+ """GetSocialAPI Facebook client.
9
+
10
+ Usage:
11
+ from getsocialapi_facebook import Client
12
+ client = Client(api_key="gsa_live_...")
13
+ group = client.resolve("https://www.facebook.com/groups/freegarden/")
14
+ client.fb.members(group["group_id"], first=15)
15
+ """
16
+
17
+ def __init__(self, api_key=None, base_url=DEFAULT_BASE_URL, timeout=30,
18
+ max_retries=3, session=None):
19
+ api_key = api_key or os.environ.get("GETSOCIALAPI_FB_API_KEY")
20
+ if not api_key:
21
+ raise ValueError(
22
+ "api_key is required (pass api_key=... or set GETSOCIALAPI_FB_API_KEY)"
23
+ )
24
+ self._transport = Transport(
25
+ base_url=base_url, api_key=api_key, timeout=timeout,
26
+ max_retries=max_retries, session=session,
27
+ )
28
+ self.fb = self.facebook = FacebookAPI(self._transport)
@@ -0,0 +1,58 @@
1
+ class GetSocialFacebookError(Exception):
2
+ """Base error for all GetSocialAPI Facebook SDK failures."""
3
+
4
+ def __init__(self, message, response=None, body=None):
5
+ super().__init__(message)
6
+ self.response = response
7
+ self.body = body if body is not None else {}
8
+
9
+
10
+ def _reason(body, fallback):
11
+ """Extract a human-readable reason: {"error": "..."} | {"error": {"message"}} | {"message": ..."}."""
12
+ err = body.get("error")
13
+ if isinstance(err, dict):
14
+ err = err.get("message")
15
+ if isinstance(err, str) and err:
16
+ return err
17
+ msg = body.get("message")
18
+ if isinstance(msg, str) and msg:
19
+ return msg
20
+ return fallback
21
+
22
+
23
+ class BadRequestError(GetSocialFacebookError):
24
+ """400 — bad parameters."""
25
+
26
+ def __init__(self, response=None, body=None):
27
+ body = body or {}
28
+ super().__init__(_reason(body, "Bad request"), response, body)
29
+
30
+
31
+ class APIKeyError(GetSocialFacebookError):
32
+ """401 — invalid API key."""
33
+
34
+ def __init__(self, response=None, body=None):
35
+ super().__init__("Invalid API key", response, body)
36
+
37
+
38
+ class RateLimitError(GetSocialFacebookError):
39
+ """429 — rate limit (body: {"error": "rate limit", "limit": N})."""
40
+
41
+ def __init__(self, response=None, body=None, limit=None):
42
+ super().__init__(f"Rate limit exceeded (limit: {limit})", response, body)
43
+ self.limit = limit
44
+
45
+
46
+ class UpstreamError(GetSocialFacebookError):
47
+ """502 — temporary upstream error. Automatically retried."""
48
+
49
+ def __init__(self, response=None, body=None):
50
+ body = body or {}
51
+ super().__init__(_reason(body, "Temporary upstream error"), response, body)
52
+
53
+
54
+ class ServiceUnavailableError(GetSocialFacebookError):
55
+ """503 — service overloaded."""
56
+
57
+ def __init__(self, response=None, body=None):
58
+ super().__init__("Service overloaded", response, body)
@@ -0,0 +1,152 @@
1
+ from urllib.parse import quote
2
+
3
+
4
+ def _quote(value):
5
+ return quote(str(value), safe="")
6
+
7
+
8
+ def _digits(value, name, max_len=20):
9
+ s = str(value)
10
+ if not s.isdigit() or not 1 <= len(s) <= max_len:
11
+ raise ValueError(f"{name} must be 1-{max_len} digits, got: {value!r}")
12
+ return s
13
+
14
+
15
+ class FacebookAPI:
16
+ """Facebook endpoints of GetSocialAPI (groups, profiles, connections)."""
17
+
18
+ def __init__(self, transport):
19
+ self._t = transport
20
+
21
+ # ---------- Groups ----------
22
+
23
+ def resolve(self, url):
24
+ """Resolve a group link (numeric id, slug, m.facebook, share/g) to group_id."""
25
+ s = str(url).strip()
26
+ if not s:
27
+ raise ValueError("url must not be empty")
28
+ return self._t.request("GET", "/resolve", params={"url": s})
29
+
30
+ def resolve_profile(self, url):
31
+ """Resolve a profile link to a numeric user_id."""
32
+ s = str(url).strip()
33
+ if not s:
34
+ raise ValueError("url must not be empty")
35
+ return self._t.request("GET", "/resolve/profile", params={"url": s})
36
+
37
+ def overview(self, group_id):
38
+ """Group card: name, visibility, admins count. Upstream: FetchGroupMemberList."""
39
+ return self._t.request("GET", "/overview", params={"group_id": _digits(group_id, "group_id")})
40
+
41
+ def members(self, group_id, after=None):
42
+ """Group members, raw Facebook GraphQL (one page of 15).
43
+
44
+ Page 1 -> data.group, page 2+ -> data.fetch__Group. The page size is
45
+ fixed by the service (15 edges); pass page_info.end_cursor as `after`
46
+ while has_next_page is true to walk the list.
47
+ """
48
+ params = {"group_id": _digits(group_id, "group_id")}
49
+ if after is not None:
50
+ params["after"] = after
51
+ return self._t.request("GET", "/api/v1/members", params=params)
52
+
53
+ def group_posts(self, group_id, after=None):
54
+ """Group feed posts, raw GroupFeedCsrQuery payload (2-3 posts/page).
55
+
56
+ Story-shaped posts (creation_time, message.text, actors,
57
+ attachments) — same node schema as the user timeline; skip
58
+ GroupsSectionHeaderUnit edges. Take the cursor from
59
+ data.group_address.group_feed.page_info.end_cursor and pass it
60
+ back as `after` while has_next_page is true. Post visibility in
61
+ closed groups depends on the scraper token's membership.
62
+ """
63
+ params = {"group_id": _digits(group_id, "group_id")}
64
+ if after is not None:
65
+ params["after"] = after
66
+ return self._t.request("GET", "/api/v1/group_posts", params=params)
67
+
68
+ # ---------- Profile ----------
69
+
70
+ def contacts(self, user_id):
71
+ """Profile About (contacts) — the untouched Facebook GraphQL payload.
72
+
73
+ Walk data.user.profile_intro_card: each About section is a connection
74
+ with edges[].node; `field_type` describes the entry kind (email,
75
+ phone, gender, employer, ...), linked entities are full node objects.
76
+ """
77
+ return self._t.request("GET", "/api/v1/contacts",
78
+ params={"user_id": _digits(user_id, "user_id")})
79
+
80
+ def posts(self, user_id, after=None):
81
+ """User timeline posts, raw Facebook GraphQL (one page of ~100).
82
+
83
+ data.profile.timeline_feed_units.edges[] holds Story nodes;
84
+ take the cursor from timeline_feed_units.page_info.end_cursor
85
+ and pass it back as `after` while has_next_page is true. The
86
+ cursor is a ~888-char base64 string — pass it verbatim (the
87
+ transport URL-encodes it). Cursors are reusable and not bound
88
+ to a session. Use only the page_info cursor — end_cursor values
89
+ inside node attachments are impostors and yield Invalid Cursor.
90
+ """
91
+ return self._t.request("GET", "/api/v1/posts", params=_conn(user_id, after))
92
+
93
+ def feed(self, user_id=None, group_id=None, per_page=None, after=None):
94
+ """GET /api/v1/feed — unified user/group feed, small pages.
95
+
96
+ Pass exactly one of `user_id` / `group_id`. Page size is
97
+ `per_page` (integer 3-20, default 10); a page may come back
98
+ short — follow has_next_page instead of counting posts.
99
+
100
+ user: `data.profile.timeline_feed_units` (~10 Story nodes;
101
+ Facebook may return slightly more — the service does not trim,
102
+ trimming would lose posts at page seams).
103
+ group: `data.group_address.group_feed` (Facebook caps its own
104
+ page at ~2 posts; the service stitches several FB pages up to
105
+ per_page — up to 2 duplicate edges are possible at the seam,
106
+ dedupe by node.cache_id).
107
+
108
+ Cursor: page_info.end_cursor of the respective connection,
109
+ passed back as `after` while has_next_page is true. A broken
110
+ cursor raises BadRequestError (400 Invalid Cursor).
111
+ """
112
+ if (user_id is None) == (group_id is None):
113
+ raise ValueError("pass exactly one of user_id or group_id")
114
+ params = {}
115
+ if user_id is not None:
116
+ params["user_id"] = _digits(user_id, "user_id")
117
+ else:
118
+ params["group_id"] = _digits(group_id, "group_id")
119
+ if per_page is not None:
120
+ if isinstance(per_page, bool) or not isinstance(per_page, int) \
121
+ or not 3 <= per_page <= 20:
122
+ raise ValueError(f"per_page must be an integer 3-20, "
123
+ f"got: {per_page!r}")
124
+ params["per_page"] = per_page
125
+ if after is not None:
126
+ params["after"] = after
127
+ return self._t.request("GET", "/api/v1/feed", params=params)
128
+
129
+ # ---------- Connections ----------
130
+
131
+ def followers(self, user_id, after=None):
132
+ """User followers. Raw GraphQL: data.full_profile_list.list_items."""
133
+ return self._t.request("GET", "/api/v1/followers",
134
+ params=_conn(user_id, after))
135
+
136
+ def followings(self, user_id, after=None):
137
+ """User followings."""
138
+ return self._t.request("GET", "/api/v1/followings",
139
+ params=_conn(user_id, after))
140
+
141
+ def friends(self, user_id, after=None):
142
+ """User friends."""
143
+ return self._t.request("GET", "/api/v1/friends",
144
+ params=_conn(user_id, after))
145
+
146
+
147
+ def _conn(user_id, after):
148
+ params = {"user_id": _digits(user_id, "user_id")}
149
+ if after is not None:
150
+ params["after"] = after
151
+ return params
152
+
@@ -0,0 +1,77 @@
1
+ import time
2
+
3
+ import requests
4
+
5
+ from .exceptions import (
6
+ APIKeyError,
7
+ BadRequestError,
8
+ GetSocialFacebookError,
9
+ RateLimitError,
10
+ ServiceUnavailableError,
11
+ UpstreamError,
12
+ )
13
+
14
+ DEFAULT_BASE_URL = "https://api.getsocialapi.com/fb"
15
+
16
+ RETRYABLE_STATUS = {429, 500, 502, 503, 504}
17
+
18
+
19
+ class Transport:
20
+ """HTTP transport: X-API-Key auth, JSON parsing, retries."""
21
+
22
+ def __init__(self, base_url=DEFAULT_BASE_URL, api_key=None, timeout=30,
23
+ max_retries=3, session=None):
24
+ self.base_url = base_url.rstrip("/")
25
+ self.api_key = api_key
26
+ self.timeout = timeout
27
+ self.max_retries = max_retries
28
+ self.session = session or requests.Session()
29
+
30
+ def request(self, method, path, params=None):
31
+ url = f"{self.base_url}{path}"
32
+ headers = {"X-API-Key": self.api_key}
33
+ attempts = self.max_retries + 1
34
+ for attempt in range(attempts):
35
+ try:
36
+ resp = self.session.request(
37
+ method, url, params=params, headers=headers, timeout=self.timeout,
38
+ )
39
+ except (requests.ConnectionError, requests.Timeout) as e:
40
+ if attempt < attempts - 1:
41
+ time.sleep(min(2 ** attempt, 10))
42
+ continue
43
+ raise GetSocialFacebookError(f"Network error: {e}") from e
44
+
45
+ if resp.status_code in RETRYABLE_STATUS and attempt < attempts - 1:
46
+ time.sleep(self._retry_after(resp))
47
+ continue
48
+ return self._parse(resp)
49
+ raise GetSocialFacebookError("Request failed after retries") # pragma: no cover
50
+
51
+ @staticmethod
52
+ def _retry_after(resp):
53
+ try:
54
+ return min(max(int(float(resp.headers.get("Retry-After", 1))), 0), 30)
55
+ except (TypeError, ValueError):
56
+ return 1
57
+
58
+ @staticmethod
59
+ def _parse(resp):
60
+ try:
61
+ body = resp.json()
62
+ except ValueError:
63
+ body = {"raw": resp.text}
64
+
65
+ if resp.status_code == 400:
66
+ raise BadRequestError(resp, body)
67
+ if resp.status_code == 401:
68
+ raise APIKeyError(resp, body)
69
+ if resp.status_code == 429:
70
+ raise RateLimitError(resp, body, limit=(body or {}).get("limit"))
71
+ if resp.status_code == 502:
72
+ raise UpstreamError(resp, body)
73
+ if resp.status_code == 503:
74
+ raise ServiceUnavailableError(resp, body)
75
+ if resp.status_code >= 400:
76
+ raise GetSocialFacebookError(f"HTTP {resp.status_code}: {body}", resp, body)
77
+ return body
@@ -0,0 +1,157 @@
1
+ Metadata-Version: 2.4
2
+ Name: getsocialapi-facebook
3
+ Version: 0.1.0
4
+ Summary: GetSocialAPI — Python client for the Facebook Scraper API (groups, profiles, connections)
5
+ Author: GetSocialAPI
6
+ License: MIT
7
+ Project-URL: Homepage, https://getsocialapi.com
8
+ Project-URL: Documentation, https://getsocialapi.com/docs/
9
+ Keywords: facebook,api,scraping,sdk,groups,members
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: requests>=2.25
19
+ Dynamic: license-file
20
+
21
+ # Facebook Scraper API
22
+
23
+ [![tests](https://github.com/GetSocialAPI/Facebook-Scraper-API/actions/workflows/tests.yml/badge.svg)](https://github.com/GetSocialAPI/Facebook-Scraper-API/actions/workflows/tests.yml)
24
+ [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
25
+ [![python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org)
26
+
27
+ Python client for the **[GetSocialAPI](https://getsocialapi.com/)** Facebook Scraper API.
28
+
29
+ - Groups: resolve links, overview, paginated members
30
+ - Profiles: resolve links, About/contacts in raw GraphQL
31
+ - Connections: followers, followings, friends
32
+ - Raw responses — no transformations
33
+ - Automatic retries on 429/502/503, typed exceptions
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install git+https://github.com/GetSocialAPI/Facebook-Scraper-API.git
39
+ ```
40
+
41
+ ## Quickstart
42
+
43
+ ```python
44
+ from getsocialapi_facebook import Client
45
+
46
+ # Get your API key at https://getsocialapi.com/dashboard/
47
+ client = Client(api_key="gsa_live_...") # or set GETSOCIALAPI_FB_API_KEY
48
+
49
+ # Resolve any group link (slug, m.facebook, share/g) to a group_id
50
+ group = client.fb.resolve("https://www.facebook.com/groups/freegarden/")
51
+ group_id = group["group_id"]
52
+
53
+ # Profile links resolve the same way
54
+ user = client.fb.resolve_profile("https://www.facebook.com/nasa")
55
+ user_id = user["user_id"]
56
+
57
+ # Group card
58
+ client.fb.overview(group_id)
59
+
60
+ # Members: raw GraphQL pages (1st -> data.group, 2nd+ -> data.fetch__Group)
61
+ page = client.fb.members(group_id) # fixed page of 15 members
62
+ page["data"]["group"]["group_member_profiles"]["page_info"]["end_cursor"]
63
+
64
+ # Next page: pass page_info.end_cursor as `after`
65
+ client.fb.members(group_id, after="AQHTm5Xq…") # end_cursor from page_info
66
+ ```
67
+
68
+ Base URL: `https://api.getsocialapi.com/fb` — authenticate with the API key
69
+ from your [dashboard](https://getsocialapi.com/dashboard/).
70
+
71
+ ## Client
72
+
73
+ ```python
74
+ Client(api_key=None, base_url="https://api.getsocialapi.com/fb", timeout=30, max_retries=3)
75
+ ```
76
+
77
+ - `api_key` — your API key (env var `GETSOCIALAPI_FB_API_KEY` works too)
78
+
79
+ ## Methods
80
+
81
+ | Method | Route | Description |
82
+ | --- | --- | --- |
83
+ | `resolve(url)` | `GET /resolve` | group link → `group_id` (numeric id, slug, m.facebook, `/share/g/…`) |
84
+ | `resolve_profile(url)` | `GET /resolve/profile` | profile link → `user_id` |
85
+ | `overview(group_id)` | `GET /overview` | group card: name, visibility |
86
+ | `members(group_id, after=None)` | `GET /api/v1/members` | raw GraphQL page of 15 (1st — `data.group`, 2nd+ — `data.fetch__Group`) |
87
+ | `group_posts(group_id, after=None)` | `GET /api/v1/group_posts` | group feed: page of 2–3 `Story` posts, cursor from `group_feed.page_info.end_cursor` |
88
+ | `contacts(user_id)` | `GET /api/v1/contacts` | profile About: raw GraphQL |
89
+ | `posts(user_id, after=None)` | `GET /api/v1/posts` | user timeline: page of ~100 posts (the fixed service maximum), cursor from `timeline_feed_units.page_info.end_cursor` |
90
+ | `feed(user_id=None, group_id=None, per_page=None, after=None)` | `GET /api/v1/feed` | unified user/group feed, small pages: `per_page` 3–20 (default 10) |
91
+ | `followers(user_id, after=None)` | `GET /api/v1/followers` | raw GraphQL `full_profile_list` |
92
+ | `followings(user_id, after=None)` | `GET /api/v1/followings` | same shape |
93
+ | `friends(user_id, after=None)` | `GET /api/v1/friends` | same shape |
94
+
95
+ Full request/response reference with live examples:
96
+ [getsocialapi.com/docs — API Reference / Facebook](https://getsocialapi.com/docs/api/facebook/resolve/).
97
+
98
+ ### Pagination
99
+
100
+ Every method returns one raw page. Pass the connection's
101
+ `page_info.end_cursor` as `after` while `has_next_page` is true.
102
+
103
+ | List | Page size (fixed) | Cursor source |
104
+ | --- | --- | --- |
105
+ | `posts` | ~100 posts — the service maximum, there is no `count` parameter | `data.profile.timeline_feed_units.page_info.end_cursor` |
106
+ | `feed` | `per_page` 3–20 (default 10); a page may come back short — follow `has_next_page` | same connection as the target: `timeline_feed_units` / `group_feed` `page_info.end_cursor` |
107
+ | `group_posts` | 2–3 `Story` posts (skip `GroupsSectionHeaderUnit` edges, dedupe by `cache_id`) | `data.group_address.group_feed.page_info.end_cursor` |
108
+ | `members` | 15 | `data.(group / fetch__Group).group_member_profiles.page_info.end_cursor` |
109
+ | `followers` / `followings` / `friends` | 15 | `…list_items.page_info.end_cursor` |
110
+
111
+ Cursors are reusable and not bound to a session; a broken or foreign cursor
112
+ returns `400 Invalid Cursor` — take `end_cursor` only from the connection's
113
+ `page_info`, never from node fields.
114
+
115
+ ```python
116
+ posts, after = [], None
117
+ while True:
118
+ page = client.fb.posts(user_id, after=after)
119
+ tfu = page["data"]["profile"]["timeline_feed_units"]
120
+ posts += [e["node"] for e in tfu["edges"]]
121
+ pi = tfu.get("page_info") or {}
122
+ if not pi.get("has_next_page") or not pi.get("end_cursor"):
123
+ break
124
+ after = pi["end_cursor"]
125
+ ```
126
+
127
+ ### Response notes
128
+
129
+ - members page 1 lives in `data.group`, following pages in `data.fetch__Group`
130
+ - connection lists: `can_see_follower_list: false` means the list is hidden by
131
+ privacy settings (only the count is available)
132
+
133
+ ## Errors
134
+
135
+ | Exception | HTTP | Notes |
136
+ | --- | --- | --- |
137
+ | `GetSocialFacebookError` | — | base class; also network errors |
138
+ | `BadRequestError` | 400 | bad parameters |
139
+ | `APIKeyError` | 401 | invalid key |
140
+ | `RateLimitError` | 429 | `.limit` from the response |
141
+ | `UpstreamError` | 502 | temporary upstream error — retried automatically |
142
+ | `ServiceUnavailableError` | 503 | service overloaded — retried automatically |
143
+
144
+ Only successful responses count against your quota.
145
+ Plans and rate limits: [getsocialapi.com/#pricing](https://getsocialapi.com/#pricing).
146
+
147
+ ## Support
148
+
149
+ - Telegram: [@getsocialapi](https://t.me/getsocialapi)
150
+ - Email: [hello@getsocialapi.com](mailto:hello@getsocialapi.com)
151
+ - Website: [getsocialapi.com](https://getsocialapi.com/)
152
+ - Documentation: [getsocialapi.com/docs](https://getsocialapi.com/docs/)
153
+ - Status: [getsocialapi.com/status](https://getsocialapi.com/status/)
154
+
155
+ ## License
156
+
157
+ MIT
@@ -0,0 +1,10 @@
1
+ getsocialapi_facebook/__init__.py,sha256=65B_2cEW3f-0PuH8fubBhvvIoxEQXUNP4QvWiRsFH7A,440
2
+ getsocialapi_facebook/client.py,sha256=Oa7aboxkTxhut0sQMpWe1lqMy4tKQdhO8o9h8wHmgi4,974
3
+ getsocialapi_facebook/exceptions.py,sha256=kzEhma36WPvF5_9pXwqpYL35-kjMFr6y4xTIY3Hb01E,1876
4
+ getsocialapi_facebook/facebook.py,sha256=vLvPk_A4IeJpd5lpb40fGdQmd1q--fpbxkKsPsFwTAA,6401
5
+ getsocialapi_facebook/http.py,sha256=7pF7tqL4e0NIlGfTU52CMD1ZHOntF7nBOMrG7_EmOWU,2581
6
+ getsocialapi_facebook-0.1.0.dist-info/licenses/LICENSE,sha256=jgv_wwS6fmja7z85ssgPuXIDkB6DTDr-v03_ClRjkyY,1069
7
+ getsocialapi_facebook-0.1.0.dist-info/METADATA,sha256=vz9rta5vzMjl5nehsvrpnJOHtlQ86YQWwBNrJGWiKPw,6790
8
+ getsocialapi_facebook-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
9
+ getsocialapi_facebook-0.1.0.dist-info/top_level.txt,sha256=AM6Py7S-OFke-Nk2WN4880ueQSV_kKs35gl6YH9fP-c,22
10
+ getsocialapi_facebook-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GetSocialAPI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ getsocialapi_facebook