Client¶
Start here. SocialChimp is the class your app creates once. Account is
what you get back for a single connection, and it is what you call .post()
and .direct on.
SocialChimp¶
SocialChimp
¶
SocialChimp(
storage: Storage,
*,
platforms: Mapping[str, Platform] | None = None,
token_manager: TokenManager | None = None,
make_lock: MakeLock | None = None,
http: HttpClient | None = None,
)
The way in. One of these is enough for a whole app.
Give it somewhere to keep connections and it does the rest: finds the platform for each network, keeps tokens working, checks posts before sending them, and closes what it opened.
sc = SocialChimp(storage=MyStorage())
step = await sc.start_login("mastodon", host="mastodon.social",
redirect_uri="https://example.com/cb")
Keep one for the life of your process. The locks that stop two workers renewing the same token at once live on it, so a new one per request protects nothing.
Attributes:
| Name | Type | Description |
|---|---|---|
storage |
Where connections and app credentials are kept. |
Set up one app's use of socialchimp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
storage
|
Storage
|
Where connections and app credentials are kept. The one thing you have to provide. |
required |
platforms
|
Mapping[str, Platform] | None
|
Ready-made platforms, by name. Anything not named here is found among the installed platforms and created with no arguments, so this is where a platform that needs settings of its own goes - and where a test puts a fake. |
None
|
token_manager
|
TokenManager | None
|
Renews tokens. Left out, one is made for each
network, which is what you want almost always. Pass your own
only if you need to change how renewal works entirely - and
note that yours has to look up app credentials itself, which
|
None
|
make_lock
|
MakeLock | None
|
Makes the lock held while a token is renewed. Pass one that every process shares - built on Redis, say - if you run more than one web or queue worker. The default only holds inside one process, so without this two workers can renew the same connection at once, and on the networks that replace the refresh token each time that disconnects the account. |
None
|
http
|
HttpClient | None
|
Sends requests for |
None
|
Source code in src/socialchimp/client.py
platform_for
¶
platform_for(name: str) -> Platform
Return the platform for one network, making it if need be.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Which network, for example |
required |
Returns:
| Type | Description |
|---|---|
Platform
|
The platform. The same one every time, so anything it remembers |
Platform
|
is kept. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If nothing is installed or registered under that name. The message lists what is, and how to install the network you asked for when it is one socialchimp covers. |
Source code in src/socialchimp/client.py
features
¶
features(platform: str) -> Feature
Ask what a network can do, by name, with no connection needed.
Useful for deciding which button to show before anyone has connected
an account. Once an account exists, Account.features is usually
the better call: this looks the platform up by name alone, so it
cannot tell you anything about that particular account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
Returns:
| Type | Description |
|---|---|
Feature
|
The features that network supports. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If nothing is installed or registered under that name. |
Source code in src/socialchimp/client.py
tokens_for
¶
tokens_for(name: str) -> TokenManager
Return the token manager for one network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Which network, for example |
required |
Returns:
| Type | Description |
|---|---|
TokenManager
|
The manager, made on first use unless you passed one in. The |
TokenManager
|
same one every time, because the locks that stop two renewals |
TokenManager
|
colliding live on it. |
Source code in src/socialchimp/client.py
http_for
¶
http_for(connection: Connection) -> HttpClient
Return the HTTP client for one connection's network and address.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection
|
Connection
|
The account whose network we are talking to. |
required |
Returns:
| Type | Description |
|---|---|
HttpClient
|
The client, made on first use unless you passed one in. One per |
HttpClient
|
network, address and event loop, so accounts on the same server |
HttpClient
|
share one. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If you call this from outside async code, where there is no loop for a client to belong to. |
Source code in src/socialchimp/client.py
fresh_connection
async
¶
fresh_connection(connection_id: str) -> Connection
Read one connection, with a token that works right now.
Every call that acts as an account goes through here first, so a token is always renewed before it is used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection_id
|
str
|
The id your app gave this connection. |
required |
Returns:
| Type | Description |
|---|---|
Connection
|
The connection, renewed first if its token was running out. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If nothing is stored under that id. |
TokenExpiredError
|
If the token needed renewing and could not be. |
Source code in src/socialchimp/client.py
account
¶
account(connection_id: str) -> Account
Return a handle for one connected account.
Cheap to make and reads nothing, so a handle for a connection that has not been saved yet is fine to hold. The connection is looked up when you actually do something with it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection_id
|
str
|
The id your app gave this connection. |
required |
Returns:
| Type | Description |
|---|---|
Account
|
The handle. |
Source code in src/socialchimp/client.py
create_app
async
¶
create_app(
platform: str,
*,
name: str,
redirect_uri: str,
host: str | None = None,
scopes: tuple[str, ...] = (),
) -> AppCredentials
Register your app with a network, and save what it gives back.
Only Mastodon can do this, and it has to be done once per server. Everywhere else you register the app by hand in a developer portal, and several networks review it before it works at all - so asking here says exactly that instead of failing later.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
name
|
str
|
The app name people see when they approve it. |
required |
redirect_uri
|
str
|
Where the network sends people back to. |
required |
host
|
str | None
|
Which server to register on, for networks with many. |
None
|
scopes
|
tuple[str, ...]
|
Permissions the app will ask for. |
()
|
Returns:
| Type | Description |
|---|---|
AppCredentials
|
The credentials, already saved through your storage. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot register an app for you. The message says where to register it by hand. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
start_login
async
¶
start_login(
platform: str,
*,
redirect_uri: str,
scopes: tuple[str, ...] = (),
host: str | None = None,
state: str | None = None,
) -> LoginStep
Begin signing someone in to a network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
redirect_uri
|
str
|
Where the network sends the person back to. It has to match what the network's developer portal has on file. |
required |
scopes
|
tuple[str, ...]
|
Permissions to ask for. Empty uses the platform's sensible defaults. |
()
|
host
|
str | None
|
Which server, for networks that have more than one. |
None
|
state
|
str | None
|
A value handed back to you at the end, so you can tell which of your users came back. One is made for you if you leave it out. |
None
|
Returns:
| Type | Description |
|---|---|
LoginStep
|
What to do next, handed back exactly as the platform gave it. |
LoginStep
|
Usually |
LoginStep
|
|
LoginStep
|
because |
LoginStep
|
there. |
LoginStep
|
Networks signed in to with an app password or a bot token answer |
LoginStep
|
with |
LoginStep
|
anybody. Show a box for each of |
LoginStep
|
marked |
LoginStep
|
as |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If your app is not registered with this network yet, on a network that needs one registered. Bluesky has no app to register, so nothing has to be saved first. |
Source code in src/socialchimp/client.py
finish_login
async
¶
finish_login(
platform: str,
*,
callback: Mapping[str, str],
redirect_uri: str,
scopes: tuple[str, ...] = (),
host: str | None = None,
state: str | None = None,
remember: RawData | None = None,
) -> LoginStep
Carry on after the person comes back from the network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
callback
|
Mapping[str, str]
|
The query values the network sent back, such as
Django's |
required |
redirect_uri
|
str
|
The same one the login was started with. |
required |
scopes
|
tuple[str, ...]
|
The same ones the login was started with. |
()
|
host
|
str | None
|
The same server the login was started on. |
None
|
state
|
str | None
|
The value you started with, if you chose one. |
None
|
remember
|
RawData | None
|
What |
None
|
Returns:
| Type | Description |
|---|---|
LoginStep
|
|
LoginStep
|
saved for you. |
LoginStep
|
which page or channel to use - show the options, then call |
LoginStep
|
|
Raises:
| Type | Description |
|---|---|
ConfigError
|
If your app is not registered with this network yet, on a network that needs one registered. Bluesky has no app to register, so nothing has to be saved first. |
Source code in src/socialchimp/client.py
choose
async
¶
choose(
platform: str,
*,
account_id: str,
resume_token: str,
redirect_uri: str,
scopes: tuple[str, ...] = (),
host: str | None = None,
state: str | None = None,
remember: RawData | None = None,
) -> LoginStep
Carry on a login after the person picked which account to use.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
account_id
|
str
|
The id of the option they picked, from
|
required |
resume_token
|
str
|
The value from |
required |
redirect_uri
|
str
|
The same one the login was started with. |
required |
scopes
|
tuple[str, ...]
|
The same ones the login was started with. |
()
|
host
|
str | None
|
The same server the login was started on. |
None
|
state
|
str | None
|
The value you started with, if you chose one. |
None
|
remember
|
RawData | None
|
The same value |
None
|
Returns:
| Type | Description |
|---|---|
LoginStep
|
|
LoginStep
|
saved for you. A network that asks twice can answer with another |
LoginStep
|
|
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network never pauses to ask, so there is nothing to carry on from. |
ConfigError
|
If your app is not registered with this network yet, on a network that needs one registered. Bluesky has no app to register, so nothing has to be saved first. |
Source code in src/socialchimp/client.py
1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 | |
answer_setup_check
¶
Answer the one-off question a network asks before it pushes.
Meta does this on Facebook, Instagram and Threads: point it at a URL of yours and it does a GET to it first, carrying a token you chose and a challenge to echo back. Get it wrong and Meta says the URL could not be verified, without saying why.
Nobody has connected an account by this point, so this takes the
network's name rather than going through Account - the same as
start_login does, and for the same reason. It is a plain function
rather than async because nothing is sent anywhere, so a
synchronous view can call it without a bridge.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
params
|
Mapping[str, str]
|
The query values from that GET, such as Django's
|
required |
verify_token
|
str
|
The token you typed into that network's own form.
Not your app secret - that one is for |
required |
Returns:
| Type | Description |
|---|---|
str
|
The challenge. Send it back as the whole body, with a 200 and a |
str
|
content type of |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network asks nothing before it starts sending, or never sends anything at all. The message says which, because what to do about them is different. |
SignatureError
|
If this is not a setup check, or the token is wrong. Answer 403 and send nothing back. |
Source code in src/socialchimp/client.py
check_signature
¶
Check a request a network pushed to us really came from it.
The body must be the raw bytes of the request, exactly as they
arrived. A signature is over those exact bytes, so a framework that
parsed the JSON and built it again has already broken it - the
spacing and the key order will not match. Read the body, check it
here, and let read_updates parse it afterwards. This is the single
most common reason a correct signature appears to fail.
This takes the network's name because the request arrives before we
know whose account it concerns; read_updates is what tells you
that.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
body
|
bytes
|
The request body, untouched. |
required |
headers
|
Mapping[str, str]
|
The request headers. Case does not matter. |
required |
secret
|
str
|
The secret you share with that network. Meta calls this the app secret, and it is not the verify token. |
required |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network never sends us anything. |
SignatureError
|
If the request cannot be trusted. Answer 401 and do nothing else with it - and say nothing about which check failed, because that only helps whoever is guessing. |
Source code in src/socialchimp/client.py
read_updates
¶
read_updates(platform: str, body: bytes) -> list[Update]
Turn a checked request into every update it carries.
Call this after check_signature has passed, never before.
All of them, not the first: Meta batches changes into one message
when it is busy, which is exactly when you least want to drop the
rest. Each update carries its own change on raw, so a handler
reads that straight rather than hunting through the message for the
change it is about.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform
|
str
|
Which network, for example |
required |
body
|
bytes
|
The request body, untouched. |
required |
Returns:
| Type | Description |
|---|---|
list[Update]
|
What happened, in the order the network listed it. Empty when |
list[Update]
|
the message carried nothing we can act on, which is not an |
list[Update]
|
error - networks send shapes we have no interest in. Each |
list[Update]
|
update names the connection it concerns. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network never sends us anything, or
its platform file was written before |
PlatformError
|
If the body is not one of that network's messages. |
Source code in src/socialchimp/client.py
aclose
async
¶
Close the HTTP clients this made.
A client you passed in yourself is left alone - it is yours, and you may still be using it.
A client whose loop has finished is let go of rather than closed,
for the same reason _forget_finished_loops gives: there is nobody
left to ask to close its sockets, and trying would raise here and
leave the rest of the clients open behind it.
Source code in src/socialchimp/client.py
Account¶
Account
¶
Account(client: SocialChimp, connection_id: str)
One connected account, and the things you can do as it.
Made by SocialChimp.account. Making one reads nothing: the connection
is looked up when you actually do something, so a handle for an account
that does not exist yet is fine to hold.
account = sc.account(connection_id)
result = await account.post(Post(text="hello"))
Every call here renews the token first, so a post never fails just because a token aged out while it sat in a queue.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
The id your app gave this connection. |
|
direct |
Your own requests to the same network as the same account. |
Point a handle at one connection, without reading anything.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
SocialChimp
|
The client this account belongs to. |
required |
connection_id
|
str
|
The id your app gave this connection. |
required |
Source code in src/socialchimp/client.py
connection
async
¶
connection() -> Connection
Read this connection, with a token that works right now.
Returns:
| Type | Description |
|---|---|
Connection
|
The connection, renewed first if its token was running out. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If nothing is stored under this id. |
TokenExpiredError
|
If the token needed renewing and could not be. |
Source code in src/socialchimp/client.py
profile
async
¶
Ask the network for this account's current name and picture.
Nothing is saved to storage here - this is for showing a fresh name and picture, or for renewing one that has gone stale, not for keeping a copy yourself.
Returns:
| Type | Description |
|---|---|
AccountProfile
|
The name and picture the network has on file right now. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot be asked for its own name and picture this way. |
Source code in src/socialchimp/client.py
limits
async
¶
limits() -> Limits
Look up what this network is allowing this account right now.
Worth reading before a burst of posts: a Mastodon server's post length is set by whoever runs it, and Instagram counts down how many posts are left today.
Returns:
| Type | Description |
|---|---|
Limits
|
The current limits. |
Source code in src/socialchimp/client.py
post
async
¶
post(post: Post) -> PostResult
Publish a post as this account.
The post is checked against the network's features and limits first, so an over-long post or a schedule the network cannot keep fails before a request is spent on it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post
|
Post
|
What to publish. |
required |
Returns:
| Type | Description |
|---|---|
PostResult
|
What the network said about the new post. |
Raises:
| Type | Description |
|---|---|
InvalidPostError
|
If the post breaks one of the network's limits. |
NotSupportedError
|
If the post needs something the network cannot do, such as scheduling. |
Source code in src/socialchimp/client.py
check_state
async
¶
check_state(post_id: str) -> PostResult
Ask the network how far it has got with a post.
Some networks keep working after they accept an upload. YouTube
encodes a video for minutes, sometimes hours; TikTok can put one in
somebody's drafts instead of publishing it. Both answer post()
before they are finished, so a result that came back PROCESSING
is not the end of the story.
The token is renewed first, the same as every other call here, so this is safe to put on a timer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The network's identifier for the post, which is what
|
required |
Returns:
| Type | Description |
|---|---|
PostResult
|
Where the post has got to now, in the same shape |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network finishes before it answers, so there is nothing to ask about. |
Source code in src/socialchimp/client.py
fetch_updates
async
¶
fetch_updates(
since: datetime | None = None,
) -> Sequence[Update]
Ask the network what has happened on this account since a moment.
For networks that never tell us anything themselves. Hand this to
socialchimp.events.Poller and it runs on a timer, works out what
is new, and delivers the same Update objects a pushing network
would have produced.
The token is renewed first, so a poller left running for weeks does not quietly stop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
since
|
datetime | None
|
Only return things newer than this. |
None
|
Returns:
| Type | Description |
|---|---|
Sequence[Update]
|
What has happened, oldest first. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot be asked. Where it
pushes instead, receive its requests with
|
Source code in src/socialchimp/client.py
read_replies
async
¶
read_replies(
post_id: str, *, whole_conversation: bool = False
) -> Sequence[Update]
Read the replies to one of this account's posts.
Different from fetch_updates, which asks the whole account what is
new. This reads one post, and can read further back than a poll
would bother to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The network's identifier for the post, which is what
|
required |
whole_conversation
|
bool
|
|
False
|
Returns:
| Type | Description |
|---|---|
Sequence[Update]
|
The replies, oldest first. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network keeps nothing an app can read this way. |
Source code in src/socialchimp/client.py
read_post
async
¶
read_post(post_id: str) -> PostDetails
Read one post back in full, not only what publishing it returned.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The network's identifier for the post or comment. |
required |
Returns:
| Type | Description |
|---|---|
PostDetails
|
The post, in full. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot be asked for a post this way. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
read_thread
async
¶
read_thread(
post_id: str,
*,
depth: int | None = None,
limit: int | None = None,
) -> Thread
Read a post together with the replies underneath it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The network's identifier for the post to read. |
required |
depth
|
int | None
|
How many reply levels to fetch. |
None
|
limit
|
int | None
|
A cap on how many replies come back. |
None
|
Returns:
| Type | Description |
|---|---|
Thread
|
The post and its replies. |
Thread
|
|
Thread
|
replies off before the end. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot be asked for a whole thread this way. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
reply
async
¶
reply(
post_id: str,
text: str,
*,
media: tuple[Media, ...] = (),
options: RawData | None = None,
) -> PostResult
Reply to any post or comment, at any depth.
post() with Post(reply_to=...) keeps working. This is the
recommended way to reply once a network provides it, because it can
do things post() cannot know to - keeping a Mastodon reply's
visibility no wider than its parent's, or mentioning the people
already in the thread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The post or comment being replied to, at any depth. |
required |
text
|
str
|
The reply's words. |
required |
media
|
tuple[Media, ...]
|
Pictures or videos to attach to the reply. |
()
|
options
|
RawData | None
|
Settings for one network only. |
None
|
Returns:
| Type | Description |
|---|---|
PostResult
|
What the network said about the new reply. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no way to reply to a comment this way. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
like
async
¶
like(post_id: str) -> LikeResult
Like a post or a comment.
Liking something already liked succeeds and does nothing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The post or comment to like. |
required |
Returns:
| Type | Description |
|---|---|
LikeResult
|
What the network said about the like. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot like a post. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
unlike
async
¶
Take back a like on a post or a comment.
Unliking something not liked succeeds and does nothing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The post or comment to unlike. |
required |
like_id
|
str | None
|
The like's own identifier, from |
None
|
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot unlike a post. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
read_likes
async
¶
List who liked a post.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The post or comment to list likes for. |
required |
after
|
str | None
|
A |
None
|
limit
|
int | None
|
A cap on how many come back. |
None
|
Returns:
| Type | Description |
|---|---|
Page[Like]
|
One page of likes. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot list who liked a post. Some networks that can like something cannot list who did. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
fetch_updates_after
async
¶
fetch_updates_after(
marker: str | None, *, limit: int | None = None
) -> UpdateBatch
Read what has happened since a marker, resumable across a restart.
Unlike fetch_updates, which takes a moment in time, this takes an
opaque marker your app stores and passes back - see
socialchimp.events.UpdateBatch. A moment in time can miss or
repeat updates around the edges; a marker cannot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
marker
|
str | None
|
The marker from the last call's
|
required |
limit
|
int | None
|
A cap on how many updates come back in this page. |
None
|
Returns:
| Type | Description |
|---|---|
UpdateBatch
|
The new updates, and a marker to store for next time. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot be polled with a marker this way. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
mark_seen
async
¶
Tell the network a marker from fetch_updates_after has been seen.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
marker
|
str
|
The marker that has been handled. |
required |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no marker to mark seen. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
read_conversations
async
¶
read_conversations(
*, after: str | None = None, limit: int | None = None
) -> Page[Conversation]
List this account's direct message conversations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
after
|
str | None
|
A |
None
|
limit
|
int | None
|
A cap on how many come back. |
None
|
Returns:
| Type | Description |
|---|---|
Page[Conversation]
|
One page of conversations. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no direct messages. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
read_messages
async
¶
read_messages(
conversation_id: str,
*,
after: str | None = None,
limit: int | None = None,
) -> Page[Message]
Read the messages in one conversation, newest first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conversation_id
|
str
|
Which conversation to read. |
required |
after
|
str | None
|
A |
None
|
limit
|
int | None
|
A cap on how many come back. |
None
|
Returns:
| Type | Description |
|---|---|
Page[Message]
|
One page of messages, newest first. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no direct messages. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
send_message
async
¶
send_message(
conversation_id: str,
text: str,
*,
options: RawData | None = None,
) -> Message
Send a message into an existing conversation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conversation_id
|
str
|
Which conversation to send into. |
required |
text
|
str
|
The message's words. |
required |
options
|
RawData | None
|
Settings for one network only, such as a Meta message tag. |
None
|
Returns:
| Type | Description |
|---|---|
Message
|
The message that was sent. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no direct messages. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
mark_read
async
¶
Mark a conversation as read.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conversation_id
|
str
|
Which conversation to mark. |
required |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no direct messages. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
start_conversation
async
¶
start_conversation(
person_ids: Sequence[str], text: str
) -> Message
Start a new conversation with one or more people.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
person_ids
|
Sequence[str]
|
Who to start it with. |
required |
text
|
str
|
The first message's words. |
required |
Returns:
| Type | Description |
|---|---|
Message
|
The message that was sent. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot start a conversation. Meta cannot: the customer has to write first. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
features
async
¶
features() -> Feature
Ask what this account's network can do.
Looks the connection up lazily, the same as every other call here, so this is safe to call before deciding which of the calls above to make.
Returns:
| Type | Description |
|---|---|
Feature
|
The features this network supports. |
Source code in src/socialchimp/client.py
delete_post
async
¶
Take a post back down again.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The network's identifier for the post, which is what
|
required |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot remove posts. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
read_stats
async
¶
read_stats(post_id: str) -> PostStats
Read how one of this account's posts is doing.
The token is renewed first, the same as every other call here, so this is safe to put on a timer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
post_id
|
str
|
The network's identifier for the post, which is what
|
required |
Returns:
| Type | Description |
|---|---|
PostStats
|
The numbers that network keeps about it. Anything it does not |
PostStats
|
count comes back as |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network keeps no numbers an app can read. |
ConfigError
|
If the platform says it can but has no method for it. |
Source code in src/socialchimp/client.py
reply_to_update
async
¶
reply_to_update(update: Update, text: str) -> None
Answer an update - a review, a question - in place.
A review or a question is not a post, so there is nothing on post()
for it. The token is renewed first, the same as every other call
here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
update
|
Update
|
The update to answer, exactly as |
required |
text
|
str
|
The reply. |
required |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has nothing to answer, or cannot answer this kind of update. |
Source code in src/socialchimp/client.py
delete_comment
async
¶
delete_comment(update: Update) -> None
Remove a comment outright.
The token is renewed first, the same as every other call here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
update
|
Update
|
The comment to remove, exactly as |
required |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network cannot remove this kind of update. |
Source code in src/socialchimp/client.py
set_comment_visibility
async
¶
set_comment_visibility(
update: Update, *, hidden: bool
) -> None
Hide a comment from public view, or show one again.
The token is renewed first, the same as every other call here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
update
|
Update
|
The comment to hide or show, exactly as |
required |
hidden
|
bool
|
|
required |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no visibility to change on this kind of update. |
Source code in src/socialchimp/client.py
get_location
async
¶
Read the business information this account holds.
Returns:
| Type | Description |
|---|---|
BusinessLocation
|
What the network currently has on file. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network keeps nothing beyond posts. |
Source code in src/socialchimp/client.py
update_location
async
¶
Change some of this account's business information.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fields
|
RawData
|
The fields to change, named the way the network's own API names them. Fields left out are left alone. |
required |
Returns:
| Type | Description |
|---|---|
BusinessLocation
|
The location as it stands after the change. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network keeps nothing beyond posts. |
Source code in src/socialchimp/client.py
verification_options
async
¶
List the ways this account's location could be verified right now.
Returns:
| Type | Description |
|---|---|
Sequence[VerificationOption]
|
What the network will offer. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no verification process. |
Source code in src/socialchimp/client.py
start_verification
async
¶
Ask the network to verify this account's location.
This is what makes the network act - mail a postcard, place a call, send a text or an email. Nothing about the proof passes through socialchimp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
One of the methods |
required |
Returns:
| Type | Description |
|---|---|
Verification
|
The verification now in progress. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no verification process. |
Source code in src/socialchimp/client.py
complete_verification
async
¶
Finish a verification with the code the business owner was sent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
verification_id
|
str
|
The id |
required |
pin
|
str
|
The code the business owner received. |
required |
Returns:
| Type | Description |
|---|---|
Verification
|
The verification's new state. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no verification process. |
Source code in src/socialchimp/client.py
verification_state
async
¶
Ask where this account's location's verification stands.
Returns:
| Type | Description |
|---|---|
str
|
The network's own word for the state. |
Raises:
| Type | Description |
|---|---|
NotSupportedError
|
If this network has no verification process. |
Source code in src/socialchimp/client.py
Sending your own request¶
account.direct sends a request of your own to the same network, through the
same token, the same retries and the same rate-limit handling. Only the
request itself is yours - see the tutorial
for why this exists.
Direct
¶
Direct(client: SocialChimp, connection_id: str)
Your own requests to a network, sent as one connected account.
Reached through Account.direct. The token is renewed before every
request, and retries and rate limits are handled exactly as they are for
post(). Only the request itself is yours.
reply = await account.direct.post(
"/api/v1/statuses",
json={"status": "hello", "visibility": "unlisted"},
)
Paths are joined onto the address the platform gives for this account - the account's own server for Mastodon, the one address everybody uses for Facebook. Pass a whole address instead and it is used as it is.
Point direct access at one connected account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
SocialChimp
|
The client this account belongs to. |
required |
connection_id
|
str
|
The id your app gave this connection. |
required |
Source code in src/socialchimp/client.py
request
async
¶
request(
method: str,
path: str,
*,
headers: Mapping[str, str] | None = None,
**kwargs: object,
) -> Response
Send a request as this account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
required |
path
|
str
|
Joined onto the address the platform gives for this account. |
required |
headers
|
Mapping[str, str] | None
|
Sent along with the ones the platform set. A header you set here wins, so a request that has to be signed some other way is still yours to send. |
None
|
**kwargs
|
object
|
Anything |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
The reply, which is always one the network was happy with. |
Raises:
| Type | Description |
|---|---|
SocialChimpError
|
If the network refused, or could not be
reached. See |
Source code in src/socialchimp/client.py
get
async
¶
Send a GET request as this account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Joined onto the address the platform gives for this account. |
required |
headers
|
Mapping[str, str] | None
|
Sent along with the ones the platform set. |
None
|
**kwargs
|
object
|
Anything |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
The reply. |
Source code in src/socialchimp/client.py
post
async
¶
Send a POST request as this account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Joined onto the address the platform gives for this account. |
required |
headers
|
Mapping[str, str] | None
|
Sent along with the ones the platform set. |
None
|
**kwargs
|
object
|
Anything |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
The reply. |
Source code in src/socialchimp/client.py
put
async
¶
Send a PUT request as this account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Joined onto the address the platform gives for this account. |
required |
headers
|
Mapping[str, str] | None
|
Sent along with the ones the platform set. |
None
|
**kwargs
|
object
|
Anything |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
The reply. |
Source code in src/socialchimp/client.py
delete
async
¶
Send a DELETE request as this account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Joined onto the address the platform gives for this account. |
required |
headers
|
Mapping[str, str] | None
|
Sent along with the ones the platform set. |
None
|
**kwargs
|
object
|
Anything |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
The reply. |
Source code in src/socialchimp/client.py
json
async
¶
json(
method: str,
path: str,
*,
headers: Mapping[str, str] | None = None,
**kwargs: object,
) -> RawData
Send a request as this account and read the reply as JSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
required |
path
|
str
|
Joined onto the address the platform gives for this account. |
required |
headers
|
Mapping[str, str] | None
|
Sent along with the ones the platform set. |
None
|
**kwargs
|
object
|
Anything |
{}
|
Returns:
| Type | Description |
|---|---|
RawData
|
The reply, parsed. |
Raises:
| Type | Description |
|---|---|
PlatformError
|
If the reply was not a JSON object. |
SocialChimpError
|
If the network refused the request. |
Source code in src/socialchimp/client.py
Posting to more than one account¶
There is nothing here for it, on purpose. Account.post posts as one account
and raises if that account fails; looping over your accounts, and deciding
what one failure means for the rest, is your app's job. See
the tutorial.
Keeping tokens working¶
SocialChimp uses this to renew a token a little before it runs out, taking
a lock first so two workers renewing the same connection at once cannot
disconnect an account. You will not normally construct this yourself.
TokenManager
¶
TokenManager(
storage: Storage,
get_new_token: GetNewToken,
*,
refresh_before_seconds: float = DEFAULT_REFRESH_BEFORE_SECONDS,
make_lock: MakeLock = _lock_within_this_process,
)
Hands out connections whose token is usable right now.
Ask it for a connection and it either gives you the one you have, or renews the token first, saves it, and gives you that.
tokens = TokenManager(storage, renew)
connection = await tokens.valid_token("conn-1")
One of these can be shared by everything in your process, and should be:
the locks that stop two renewals colliding live on the instance, so a new
TokenManager per request protects nothing.
Set up token renewal for one app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
storage
|
Storage
|
Where connections are read from and written back to. |
required |
get_new_token
|
GetNewToken
|
Asks a network for a new token. Wrap
|
required |
refresh_before_seconds
|
float
|
How long before a token runs out to renew it. The default of 60 seconds leaves room for a slow request. |
DEFAULT_REFRESH_BEFORE_SECONDS
|
make_lock
|
MakeLock
|
Makes the lock used while renewing one connection. The default only holds inside this process; pass your own, backed by something like Redis, if you run more than one. |
_lock_within_this_process
|
Source code in src/socialchimp/tokens.py
on_token_renewed
¶
Ask to be told whenever a token was renewed.
Handy for logging, or for warming a cache of your own. socialchimp has already saved the connection by the time you hear about it, so there is nothing you must do.
Anything your listener raises is logged and dropped. A listener watches; it never gets to fail a renewal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
listener
|
TokenRenewed
|
Called with the connection carrying its new token. |
required |
Source code in src/socialchimp/tokens.py
valid_token
async
¶
valid_token(connection_id: str) -> Connection
Return a connection whose token works right now.
Renews the token first if it is close to running out. Safe to call from anywhere, as often as you like - a connection that is fine costs one read.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection_id
|
str
|
The id your app gave this connection. |
required |
Returns:
| Type | Description |
|---|---|
Connection
|
The connection, with a token that is good for a while yet. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If no connection is stored under that id. |
TokenExpiredError
|
If the token needed renewing and could not be, because there is no refresh token, because the refresh token has itself run out, or because the network refused the one we have. The person has to sign in again. |