For a coding assistant¶
This page is written for a model generating socialchimp code, not for a person browsing the site. It is dense on purpose. For prose written for people, see the tutorial and getting started.
This describes socialchimp 0.9.0. socialchimp is
pre-1.0: a change to the middle version number can break something. Check
the version installed (socialchimp.__version__) against the version this
page names before trusting anything below, and prefer
networks.json over
guessing at a network's features, options or limits - it is generated from
the code, this page is not.
The four things¶
- A connection (
Connection) is one social account someone connected - an id, which network, a token, and anything else that network needs (a Facebook page id, a YouTube channel id). Your app saves it; socialchimp never does. - Storage is five methods your own class provides -
get_connection,save_connection,delete_connection,get_app,save_app- so socialchimp has somewhere to read and write connections and app credentials without owning your database. Start fromInMemoryStorageand replace it later. A blocking storage (Django's ORM, a plainpsycopgcursor) goes throughsync_storage, unchanged, so it runs on a thread instead of blocking the event loop. - A platform is one class per network (
MastodonPlatform,BlueskyPlatform, ...) built with no arguments and holding no per-account state. It knows how to sign someone in, publish, renew a token, and report what the network allows. Never call a platform's own methods directly unless you are writing one - go throughSocialChimpandAccount, which checkFeatures and limits first and keep tokens fresh for you. - An update (
Update) is one shape for "something happened" - a comment, a like, a new follower - whether the network pushed it to a URL of yours or socialchimp had to poll for it on a timer. Your handler code never learns which of those happened;Dispatcher.delivercalls every registered handler with the sameUpdateeither way.
SocialChimp ties these together. sc.account(connection_id) returns an
Account bound to one connection; account.post(...), account.limits(),
account.check_state(...), account.fetch_updates(...),
account.read_stats(...) and account.delete_post(...) are what an app
calls day to day.
The four sign-in shapes¶
A sign-in is a sequence of LoginStep values, always ending in Finished.
Match on the type you get back rather than assuming which one:
SendToNetwork- the ordinary case. Redirect the person to.url; they come back through yourredirect_uriand you callsc.finish_login(...)with what the network sent back.AskForDetails- for a network with no sign-in page to redirect to. Bluesky is the only one built in: there is no developer portal and no app, a person signs in with their handle and an app password they made themselves, and.fieldssays what to show them in a form of your own.LoginRequest.appisNoneon this path, and nothing is read from or saved to storage before it - a platform that takes this path listsFeature.NEEDS_NO_APP, and one that does not is refused without stored app credentials exactly as before.ChooseAccount- a pause. The person approved your app, but the network has several things it could mean (a Facebook page, a YouTube channel) and.optionslists them. Show the choice, then callsc.choose(...)with the id they picked and.resume_tokenhanded straight back - treat that token as a secret, the same asSendToNetwork.remember, because on some networks it carries the actual credentials.Finished- done..connectionis what you save.
sc.create_app(...) is a separate, earlier step some networks need before
any of this - only Mastodon can do it today (Feature.CREATE_APP); every
other network is registered by hand in a developer portal, and
sc.create_app(...) against one of those raises NotSupportedError naming
where to go instead, rather than pretending. A platform having a
create_app method is not proof it works - Facebook, Instagram and Threads
all have one, and it always raises, on purpose, so a call that reaches it by
mistake gets a real explanation instead of AttributeError. Feature.CREATE_APP
is the fact to trust, not whether the method exists; /networks.json already
makes that distinction for you.
socialchimp raises. Your app handles.¶
Every problem comes back as an exception raised at the call that caused it,
never written down and handed back as data. Catch SocialChimpError to
catch everything:
| Error | What it means |
|---|---|
ConfigError |
Something is wrong on your side - a bad platform name, missing credentials, a datetime with no timezone. Also a ValueError. Fix the code; do not retry. |
InvalidPostError |
The post itself breaks a rule - too long, too many pictures, empty, a picture socialchimp could not read. Also a ValueError. Caught before sending where possible, so this is usually not a wasted request. |
NotSupportedError |
The network genuinely cannot do this - Bluesky cannot schedule, YouTube has no text-only post. Not a gap in socialchimp. .suggestion often says what to do instead. |
AuthError |
The network would not accept the token. Usually means the person has to connect their account again. |
TokenExpiredError |
An AuthError: renewal was tried and failed (no refresh token, or it is itself expired/revoked). |
NotAllowedError |
The account is real but lacks a permission. Ask for the right scope next time someone connects. |
NotFoundError |
The post, account or page asked for does not exist. |
RateLimitError |
The network wants you to slow down. .retry_after gives seconds, when the network says. |
NetworkError |
The network was never reached - a timeout, a dropped connection - after socialchimp already retried. Worth retrying later, unlike the others. |
SignatureError |
An incoming webhook request did not check out. Answer 401 and do nothing else with it. |
PlatformError |
The network answered with an error socialchimp has no better name for yet. .raw holds what it actually said. |
Two error types deliberately double as ValueError (ConfigError,
InvalidPostError) because they are raised for a bad value your own code
handed in, not for anything a network said - see the comment above
ConfigError in socialchimp/errors.py.
Posting to several accounts is your loop, not socialchimp's¶
There is no method that posts to more than one account. Write the loop:
from socialchimp import SocialChimpError
for connection_id in (mastodon_id, bluesky_id):
try:
result = await sc.account(connection_id).post(post)
except SocialChimpError as failed:
print("failed:", connection_id, failed)
else:
print("posted:", result.url)
SocialChimp.post_to_many (with PostJob and PostError) was removed in
0.3.0 and has no replacement call. A model trained on older material may
reach for it; it does not exist. Whether one account failing should stop the
others, whether a failure belongs in a row for a worker to retry, whether
someone needs telling - only the app calling socialchimp knows, so that
decision was moved into the loop above rather than picked for you.
Also changed recently (0.3.0)¶
Dispatcher.delivernow raises. It used to log a failed handler and mark the update handled anyway, so a handler that always failed silently lost every update after one log line. Now: every handler still runs, but the update is marked handled only if all of them succeeded, and any that raised come back together as anExceptionGroup(except* Exception:to handle it). A webhook route (Django, FastAPI, Flask) whose handlers all fail now answers 500, not200 {"ok": true}, so the network retries an update that was never actually recorded as handled.Routes(..., secrets={...})with nodelivernow raisesConfigErrorat construction. It used to accept this silently and quietly drop every correctly signed webhook.- A webhook route's own setup mistakes (
ConfigError) now reach your error handling instead of being turned into a 500 with a JSON body. A missing secret or an app that was never registered is a bug to fix, not a network condition to retry. SocialChimpErroractually catches everything now. Before 0.3.0,socialchimp.modelsraised a bareValueErrorfor things like an emptyPost, adatetimewith no timezone, or an unreadableMedia- all of which walked straight pastexcept SocialChimpError. Those are nowInvalidPostErrororConfigError, and being also aValueErrormeans old code that caughtValueErrorstill works.
The changelog has the full 0.3.0 section, including changes narrower than these; read it before writing code that has to match this version exactly.