granary package

Reference documentation.

as1

Utilities for ActivityStreams 1 objects.

granary.as1.object_type(obj)[source]

Returns the object type, or the verb if it’s an activity object.

Details: http://activitystrea.ms/specs/json/1.0/#activity-object

Parameters:

obj (dict) – decoded JSON ActivityStreams object

Returns:

ActivityStreams object type

Return type:

str

granary.as1.get_object(obj, field='object')[source]

Extracts and returns a field value as an object.

If the field value is a string, returns an object with it as the id, eg {'id': val}. If the field value is a list, returns the first element.

Parameters:
  • obj (dict) – decoded JSON ActivityStreams object

  • field (str)

Return type:

dict

granary.as1.get_objects(obj, field='object')[source]

Extracts and returns a field’s values as objects.

If a field value is a string, generates an object with it as the id, eg {'id': val}.

Parameters:
  • obj (dict) – decoded JSON ActivityStreams object

  • field (str)

Return type:

sequence of dict

granary.as1.get_owner(obj)[source]

Returns an object’s author or actor.

Prefers author, then actor. If that’s an object, returns its id, if available. If neither field exists, but obj.objectType is in ACTOR_TYPES, returns its own id, if available.

For posts, updates, and deletes, falls back to the inner object’s owner if the outer activity has no actor.

Parameters:

obj (dict) – decoded JSON ActivityStreams object

Return type:

str

granary.as1.get_url(obj)[source]

Returns the url field’s first text value, or ''.

Somewhat duplicates microformats2.get_text().

granary.as1.get_ids(obj, field)[source]

Extracts and returns a given field’s values as ids.

Parameters:
  • obj (dict) – decoded JSON ActivityStreams object

  • field (str)

Returns string values as is. For dict values, returns their inner id or url field value, in that order of precedence.

granary.as1.merge_by_id(obj, field, new)[source]

Merges new items by id into a field in an existing AS1 object, in place.

Merges new items by id into the given field. If it exists, it must be a list. Requires all existing and new items in the field to have ids.

Parameters:
  • obj (dict) – AS1 object

  • field (str) – name of field to merge new items into

  • new (sequence of dict) – new values to merge in

granary.as1.is_public(obj, unlisted=True)[source]

Returns True if the object is public, False if private, None if unknown.

…according to the Audience Targeting extension: http://activitystrea.ms/specs/json/targeting/1.0/

Expects values generated by this library: objectType group, alias @public, @unlisted, or @private.

Also, important point: this defaults to True, ie public. Bridgy depends on that and prunes the to field from stored activities in Response objects (in bridgy/util.prune_activity). If the default here ever changes, be sure to update Bridgy’s code.

Parameters:
  • obj (dict) – AS2 activity or object

  • unlisted (bool) – whether @unlisted counts as public or not

granary.as1.add_rsvps_to_event(event, rsvps)[source]

Adds RSVP objects to an event’s attending fields, in place.

Parameters:
  • event (dict) – ActivityStreams event object

  • rsvps (sequence of dict) – ActivityStreams RSVP activity objects

granary.as1.get_rsvps_from_event(event)[source]

Returns RSVP objects for an event’s attending fields.

Parameters:

event (dict) – ActivityStreams event object

Returns:

ActivityStreams RSVP activity objects

Return type:

sequence of dict

granary.as1.activity_changed(before, after, inReplyTo=True, log=False)[source]

Returns whether two activities or objects differ meaningfully.

Only compares a few fields: objectType, verb, displayName`, ``content, summary, location, and image. Notably does not compare author, published, or updated.

Parameters:
  • before (dict) – ActivityStreams activity or object

  • after (dict) – ActivityStreams activity or object

  • inReplyTo (bool) – whether to return True if inReplyTo has changed

  • log (bool) – whether to log each changed field

Return type:

bool

granary.as1.append_in_reply_to(before, after)[source]

Appends before’s inReplyTo to after, in place.

Parameters:
  • before (dict) – ActivityStreams activity or object

  • after (dict) – ActivityStreams activity or object

granary.as1.actor_name(actor)[source]

Returns the given actor’s name if available, otherwise Unknown.

granary.as1.original_post_discovery(activity, domains=None, include_redirect_sources=True, include_reserved_hosts=True, max_redirect_fetches=None, **kwargs)[source]

Discovers original post links.

This is a variation on http://indiewebcamp.com/original-post-discovery . It differs in that it finds multiple candidate links instead of one, and it doesn’t bother looking for MF2 (etc) markup because the silos don’t let you input it. More background: https://github.com/snarfed/bridgy/issues/51#issuecomment-136018857

Original post candidates come from the upstreamDuplicates, attachments, and tags fields, as well as links and permashortlinks/permashortcitations in the text content.

Parameters:
  • activity (dict) – activity

  • domains (sequence of str) – these domains will be considered original and stored in upstreamDuplicates. (Permashortcitations are exempt.)

  • include_redirect_sources (bool) – whether to include URLs that redirect as well as their final destination URLs

  • include_reserved_hosts (bool) – whether to include domains on reserved TLDs (eg foo.example) and local hosts (eg http://foo.local/, http://my-server/)

  • max_redirect_fetches (int) – if specified, only make up to this many HTTP fetches to resolve redirects.

  • kwargs – passed to requests.head() when following redirects

Returns:

(original post URLs, mentions)

Return type:

(list of str, list of str) tuple

granary.as1.prefix_urls(activity, field, prefix)[source]

Adds a prefix to all matching URL fields, eg to inject a caching proxy.

Generally used with the image or stream fields. For example:

>>> prefix_urls({'actor': {'image': 'http://image'}}, 'image', 'https://proxy/')
{'actor': {'image': 'https://proxy/http://image'}}

Skips any URL fields that already start with the prefix. URLs are not URL-encoded before adding the prefix. (This is currently used with our caching-proxy Cloudflare worker and https://cloudimage.io/ , neither of which URL-decodes.)

Parameters:
  • activity (dict) – AS1 activity; modified in place

  • prefix (str)

granary.as1.object_urls(obj)[source]

Returns an object’s unique URLs, preserving order.

granary.as1.targets(obj)[source]

Collects an AS1 activity or object’s targets.

This is all ids/URLs that are direct “targets” of the activity, eg:

  • the post it’s replying to

  • the post it’s sharing

  • the post it’s reacting to

  • the actor or other object it’s tagging

  • the event it’s inviting someone to

  • the event it’s RSVPing to

  • the link or object it’s bookmarking

etc…

Parameters:

obj (dict) – AS1 object or activity

Returns:

targets

Return type:

list of str

as2

Convert between ActivityStreams 1 and 2, including ActivityPub.

granary.as2.get_urls(obj, key='url')[source]

Returns link['href'] or link, for each link in obj[key].

granary.as2.from_as1(obj, type=None, context='https://www.w3.org/ns/activitystreams', top_level=True)[source]

Converts an ActivityStreams 1 activity or object to ActivityStreams 2.

Parameters:
  • obj (dict) – AS1 activity or object

  • type (str) – default type if type inference can’t determine a type.

  • context (str) – included as @context

Returns:

AS2 activity or object

Return type:

dict

granary.as2.to_as1(obj, use_type=True)[source]

Converts an ActivityStreams 2 activity or object to ActivityStreams 1.

Parameters:
  • obj (dict) – AS2 activity or object

  • use_type (bool) – whether to include objectType and verb

Returns:

AS1 activity or object

Return type:

dict

granary.as2.is_public(activity, unlisted=True)[source]

Returns True if the given AS2 object or activity is public or unlisted.

https://docs.joinmastodon.org/spec/activitypub/#properties-used

Parameters:
  • activity (dict) – AS2 activity or object

  • unlisted (bool) – whether unlisted, ie public in cc instead of to counts as public or not

granary.as2.address(actor)[source]

Returns an actor’s fediverse handle aka WebFinger address aka @-@.

There’s no standard for this, it’s just a heuristic that uses preferredUsername and id or url if available, otherwise detects and transforms common user profile URLs.

Parameters:

actor (dict) – AS2 JSON actor or str actor id

Returns:

handle, eg @user@example.com, or None

Return type:

str

Adds HTML links to content for tags with startIndex and length.

content is modified in place. If content_is_html is true, does nothing. Otherwise, sets it to true if at least one link is added. Tags without startIndex/length are ignored.

TODO: duplicated in microformats2.render_content(). unify?

Parameters:

obj (dict) – AS2 JSON object

atom

Convert between ActivityStreams 1 and Atom.

Atom spec: https://tools.ietf.org/html/rfc4287 (RIP atomenabled.org)

class granary.atom.Defaulter(init={})[source]

Bases: defaultdict

Emulates Django template behavior that returns a special default value that can continue to be referenced when an attribute or item lookup fails. Helps avoid conditionals in the template itself.

https://docs.djangoproject.com/en/1.8/ref/templates/language/#variables

__init__(init={})[source]
granary.atom.activities_to_atom(activities, actor, title=None, request_url=None, host_url=None, xml_base=None, rels=None, reader=True)[source]

Converts ActivityStreams 1 activities to an Atom feed.

Parameters:
  • activities (list of dict) – ActivityStreams activities

  • actor (dict) – ActivityStreams actor, the author of the feed

  • title (str) – the feed <title> element. Defaults to User feed for [NAME]

  • request_url (str) – URL of this Atom feed, if any. Used in a link rel=”self”.

  • host_url (str) – home URL for this Atom feed, if any. Used in the top-level feed <id> element.

  • xml_base (str) – base URL, if any. Used in the top-level xml:base attribute.

  • rels (dict) – rel links to include. Keys are string ``rel``s, values are string URLs.

  • reader (bool) – whether the output will be rendered in a feed reader. Currently just includes location if True, not otherwise.

Returns:

Atom XML

Return type:

str

granary.atom.activity_to_atom(activity, xml_base=None, reader=True)[source]

Converts a single ActivityStreams 1 activity to an Atom entry.

Kwargs are passed through to activities_to_atom().

Parameters:
  • xml_base (str) – the base URL, if any. Used in the top-level xml:base attribute.

  • reader (bool) – whether the output will be rendered in a feed reader. Currently just includes location if True, not otherwise.

Returns:

Atom XML

Return type:

str

granary.atom.atom_to_activities(atom)[source]

Converts an Atom feed to ActivityStreams 1 activities.

Parameters:

atom (str) – Atom document with top-level <feed> element

Returns:

ActivityStreams activities

Return type:

list of dict

granary.atom.atom_to_activity(atom)[source]

Converts an Atom entry to an ActivityStreams 1 activity.

Parameters:

atom (str) – Atom document with top-level <entry> element

Returns:

ActivityStreams activity

Return type:

dict

granary.atom.html_to_atom(html, url=None, fetch_author=False, reader=True)[source]

Converts microformats2 HTML to an Atom feed.

Parameters:
  • html (str)

  • url (str) – URL html came from, optional

  • fetch_author (bool) – whether to make HTTP request to fetch rel-author link

  • reader (bool) – whether the output will be rendered in a feed reader. Currently just includes location if True, not otherwise.

Returns:

Atom XML

Return type:

str

granary.atom.extract_entries(atom)[source]

Extracts <entry> elements into their own separate XML documents.

Parameters:

atom (str) – Atom document with top-level <feed> or <entry> element

Returns:

Atom documents with top-level <entry> element for each entry

Return type:

list of str

bluesky

Bluesky source class.

granary.bluesky.url_to_did_web(url)[source]

Converts a URL to a did:web.

In AT Proto, only hostname-based web DIDs are supported. Paths are not supported, and will be discarded.

https://atproto.com/specs/did

Examples:

  • https://foo.com => did:web:foo.com

  • https://foo.com:3000 => did:web:foo.com

  • https://foo.bar.com/baz/baj => did:web:foo.bar.com

Parameters:

url (str)

Return type:

str

granary.bluesky.did_web_to_url(did)[source]

Converts a did:web to a URL.

In AT Proto, only hostname-based web DIDs are supported. Paths are not supported, and will throw an invalid error.

Examples:

  • did:web:foo.com => https://foo.com

  • did:web:foo.com%3A3000 => INVALID

  • did:web:bar.com:baz:baj => INVALID

https://atproto.com/specs/did

Parameters:

did (str)

Return type:

str

granary.bluesky.at_uri_to_web_url(uri, handle=None)[source]

Converts an at:// URI to a https://bsky.app URL.

https://atproto.com/specs/at-uri-scheme

Parameters:
  • uri (str) – at:// URI

  • handle – (str): optional user handle. If not provided, defaults to the DID in uri.

Returns:

https://bsky.app URL, or None

Return type:

str

Raises:

ValueError – if uri is not a string or doesn’t start with at://

granary.bluesky.web_url_to_at_uri(url, handle=None, did=None)[source]

Converts a https://bsky.app URL to an at:// URI.

https://atproto.com/specs/at-uri-scheme

Currently supports profile, post, and feed URLs with DIDs and handles, eg:

  • https://bsky.app/profile/did:plc:123abc

  • https://bsky.app/profile/vito.fyi/post/3jt7sst7vok2u

  • https://bsky.app/profile/bsky.app/feed/mutuals

If both handle and did are provided, and handle matches the URL, the handle in the resulting URI will be replaced with did.

Parameters:
  • url (str) – bsky.app URL

  • handle (str) – Bluesky handle, or None

  • did (str) – Valid DID, or None

Returns:

at:// URI, or None

Return type:

str

Raises:

ValueError – if url can’t be parsed as a bsky.app profile or post URL

granary.bluesky.from_as1_to_strong_ref(obj, client=None, value=False)[source]

Converts an AS1 object to an ATProto com.atproto.repo.strongRef.

Uses AS1 id or url`, which should be an ``at:// URI.

Parameters:
  • obj (dict) – AS1 object or activity

  • client (lexrpc.Client) – optional; if provided, this will be used to make API calls to PDSes to fetch and populate the cid field and resolve handle to DID.

  • value (bool) – whether to include the record’s value field in the returned object

Returns:

ATProto com.atproto.repo.strongRef record

Return type:

dict

granary.bluesky.from_as1_datetime(val)[source]

Converts an AS1 RFC 3339 datetime string to ATProto ISO 8601.

Bluesky requires full date and time with time zone, recommends UTC with Z suffix, fractional seconds.

https://atproto.com/specs/lexicon#datetime

Returns now (ie the current time) if the input datetime can’t be parsed.

Parameters:

val (str) – RFC 3339 datetime

Returns:

ATProto compatible ISO 8601 datetime

Return type:

str

granary.bluesky.base_object(obj)[source]

Returns the “base” Bluesky object that an object operates on.

If the object is a reply, repost, or like of a Bluesky post, this returns that post object. The id in the returned object is the AT protocol URI, while the URL is the bsky.app web URL.

Parameters:

obj (dict) – ActivityStreams object

Returns:

minimal ActivityStreams object. Usually has at least id; may also have url, author, etc.

Return type:

dict

granary.bluesky.from_as1(obj, out_type=None, blobs=None, client=None)[source]

Converts an AS1 object to a Bluesky object.

Converts to record types by default, eg app.bsky.actor.profile or app.bsky.feed.post. Use out_type to convert to a different type, eg app.bsky.actor.defs#profileViewBasic or app.bsky.feed.defs#feedViewPost.

The objectType field is required.

If a string value in an output Bluesky object is longer than its maxGraphemes or maxLength in its lexicon, it’s truncated with an ellipsis character at the end in order to fit.

Parameters:
  • obj (dict) – AS1 object or activity

  • out_type (str) – desired output lexicon $type

  • blobs (dict) – optional mapping from str URL to blob dict to use in the returned object. If not provided, or if this doesn’t have an image or similar URL in the input object, its output blob will be omitted.

  • client (Bluesky or lexrpc.Client) – optional; if provided, this will be used to make API calls to PDSes to fetch and populate CIDs for records referenced by replies, likes, reposts, etc.

Returns:

app.bsky.* object

Return type:

dict

Raises:

ValueError – if the objectType or verb fields are missing or unsupported

granary.bluesky.to_as1(obj, type=None, uri=None, repo_did=None, repo_handle=None, pds='https://bsky.social/')[source]

Converts a Bluesky object to an AS1 object.

Parameters:
  • obj (dict) – app.bsky.* object

  • type (str) – optional $type to parse with, only used if obj['$type'] is unset

  • uri (str) – optional at:// URI of this object. Used to populate the id and url fields for some object types, eg posts.

  • repo_did (str) – optional DID of the repo this object is from. Required to generate image URLs.

  • repo_handle (str) – optional handle of the user whose repo this object is from

  • pds (str) – base URL of the PDS that currently serves this object’s repo. Required to generate image URLs. Defaults to https://bsky.social/.

Returns:

AS1 object, or None if the record doesn’t correspond to an AS1 object,

eg “not found” records

Return type:

dict

Raises:

ValueError – if the $type field is missing or unsupported

granary.bluesky.blob_to_url(*, blob, repo_did, pds='https://bsky.social/')[source]

Generates a URL for a blob.

Supports both new and old style blobs: https://atproto.com/specs/data-model#blob-type

The resulting URL is a com.atproto.sync.getBlob XRPC call to the PDS.

For blobs on the official bsky.social PDS, we could consider using their CDN instead: https://av-cdn.bsky.app/img/avatar/plain/[DID]/[CID]@jpeg

They also run a resizing image proxy on cdn.bsky.social with URLs like https://cdn.bsky.social/imgproxy/[SIG]/rs:fit:2000:2000:1:0/plain/[CID]@jpeg, not sure how to generate signatures for it yet.

Parameters:
  • blob (dict)

  • repo_did (str) – DID of the repo that owns this blob

  • pds (str) – base URL of the PDS that serves this repo. Defaults to DEFAULT_PDS

Returns:

URL for this blob, or None if blob is empty or has no CID

Return type:

str

class granary.bluesky.Bluesky(handle, did=None, access_token=None, refresh_token=None, app_password=None, session_callback=None)[source]

Bases: Source

Bluesky source class. See file docstring and Source class for details.

handle
Type:

str

did
Type:

str

client
Type:

lexrpc.Client

__init__(handle, did=None, access_token=None, refresh_token=None, app_password=None, session_callback=None)[source]

Constructor.

Parameters:
  • handle (str) – username, eg snarfed.bsky.social or snarfed.org

  • did (str) – did:plc or did:web, optional

  • access_token (str) – optional

  • refresh_token (str) – optional

  • app_password (str) – optional

  • session_callback (callable, dict => None) – passed to lexrpc.Client constructor, called when a new session is created or refreshed

classmethod user_url(handle)[source]

Returns the profile URL for a given handle.

Parameters:

handle (str)

Returns:

profile URL

Return type:

str

classmethod user_to_actor(user, **kwargs)[source]

Converts a user to an actor.

Parameters:
  • user (dict) – an app.bsky.actor.profile record

  • kwargs – passed through to to_as1()

Returns:

ActivityStreams actor

Return type:

dict

classmethod post_url(handle, tid)[source]

Returns the post URL for a given handle and tid.

Parameters:
Returns:

profile URL

Return type:

str

classmethod post_id(url)[source]

Returns the at:// URI for the given URL if it’s for a post.

Also see arroba.util.parse_at_uri.

Returns:

str or None

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, include_shares=True, fetch_mentions=False, search_query=None, start_index=None, count=None, cache=None, **kwargs)[source]

Fetches posts and converts them to AS1 activities.

See Source.get_activities_response() for more information.

Bluesky-specific details:

Parameters:

activity_id (str) – an at:// URI

get_actor(user_id=None)[source]

Fetches and returns a user.

Parameters:

user_id (str) – either handle or DID; defaults to current user

Returns:

ActivityStreams actor

Return type:

dict

get_comment(comment_id, **kwargs)[source]

Fetches and returns a comment.

Parameters:
  • comment_id – string status id

  • **kwargs – unused

Returns: dict, ActivityStreams object

Raises:

ValueError – if comment_id is invalid

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a post, reply, repost, or like.

Parameters:
  • obj – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

whose content will be a dict with id, url, and type keys (all optional) for the newly created object (or None)

Return type:

CreationResult

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Preview creating a post, reply, repost, or like.

Parameters:
  • obj – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

content will be a str HTML snippet or None

Return type:

CreationResult

delete(at_uri)[source]

Deletes a record.

Parameters:

at_uri (str) – at:// URI of record delete

Returns:

content is dict with url and id fields

Return type:

CreationResult

preview_delete(at_uri)[source]

Previews deleting a record.

Parameters:

at_uri (str) – at:// URI of record delete

Return type:

CreationResult

base_object(obj)[source]

Returns the base silo object that an object operates on.

For example, if the object is a comment, this returns the post that it’s a comment on. If it’s an RSVP, this returns the event. The id in the returned object is silo-specific, ie not a tag URI.

Subclasses may override this.

Parameters:

obj (dict) – ActivityStreams object

Returns:

minimal ActivityStreams object. Usually has at least id; may also have url, author, etc.

Return type:

dict

facebook

Facebook source class. Supports both Graph API and scraping HTML.

https://developers.facebook.com/docs/graph-api/using-graph-api/

class granary.facebook.FacebookId(user, post, comment)

Bases: tuple

class granary.facebook.Facebook(access_token=None, user_id=None, scrape=False, cookie_c_user=None, cookie_xs=None)[source]

Bases: Source

Facebook source class. See file docstring and Source for details.

access_token

optional, OAuth access token

Type:

str

user_id

optional, current user’s id (either global or app-scoped)

Type:

str

scrape

whether to scrape mbasic.facebook.com’s HTML (True) or use the API (False)

Type:

bool

cookie_c_user

optional c_user cookie to use when scraping

Type:

str

cookie_xs

optional xs cookie to use when scraping

Type:

str

__init__(access_token=None, user_id=None, scrape=False, cookie_c_user=None, cookie_xs=None)[source]

Constructor.

If an OAuth access token is provided, it will be passed on to Facebook. This will be necessary for some people and contact details, based on their privacy settings.

If scrape is True, cookie_c_user and cookie_xs must be provided.

Parameters:
  • access_token (str) – optional OAuth access token

  • user_id (str) – optional, current user’s id (either global or app-scoped)

  • scrape (bool) – whether to scrape mbasic.facebook.com’s HTML (True) or use the API (False)

  • cookie_c_user (str) – optional c_user cookie to use when scraping

  • cookie_xs (str) – optional xs cookie to use when scraping

user_url(id)

Returns the URL for a user’s profile.

get_actor(user_id=None)[source]

Returns a user as a JSON ActivityStreams actor dict.

Parameters:

user_id (str) – id or username. Defaults to me, ie the current user.

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, start_index=0, count=0, etag=None, min_id=None, cache=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, fetch_events=False, fetch_mentions=False, search_query=None, fetch_news=False, event_owner_id=None, **kwargs)[source]

Fetches posts and converts them to ActivityStreams activities.

See Source.get_activities_response() for details.

Likes, top-level replies (ie comments), and reactions are always included. They come from the comments, likes, and reactions fields in the Graph API’s Post object: https://developers.facebook.com/docs/reference/api/post/

Threaded comments, ie comments in reply to other top-level comments, require an additional API call, so they’re only included if fetch_replies is True.

Mentions are never fetched or included because the API doesn’t support searching for them. https://github.com/snarfed/bridgy/issues/523#issuecomment-155523875

Most parameters are documented in Source.get_activities_response(). Additional parameters are documented here.

Parameters:
  • fetch_news (bool) – whether to also fetch and include Open Graph news stories (/USER/news.publishes). Requires the user_actions.news permission. Background in https://github.com/snarfed/bridgy/issues/479

  • event_owner_id (str) – if provided, only events owned by this user id will be returned. Avoids (but doesn’t entirely prevent) processing big non-indieweb events with tons of attendees that put us over App Engine’s instance memory limit. https://github.com/snarfed/bridgy/issues/77

get_event(event_id, owner_id=None)[source]

Returns a Facebook event post.

Parameters:
  • id (str) – site-specific event id

  • owner_id (str)

Returns:

ActivityStreams activity, or None if the event is not found or is owned by a different user than owner_id (if provided)

Return type:

dict

get_comment(comment_id, activity_id=None, activity_author_id=None, activity=None)[source]

Returns an ActivityStreams comment object.

Parameters:
  • comment_id (str) – comment id

  • activity_id (str) – activity id, optional

  • activity_author_id (str) – activity author id, optional

  • activity (dict) – activity object (optional)

Return type:

dict

get_share(activity_user_id, activity_id, share_id, activity=None)[source]

Returns an ActivityStreams share activity object.

Parameters:
  • activity_user_id (str) – id of the user who posted the original activity

  • activity_id (str) – activity id

  • share_id (str) – id of the share object

  • activity (dict) – activity object, optional

Return type:

dict

get_albums(user_id=None)[source]

Fetches and returns a user’s photo albums.

Parameters:

user_id (str) – id or username. Defaults to me, ie the current user.

Returns:

ActivityStream album objects

Return type:

sequence of dict

get_reaction(activity_user_id, activity_id, reaction_user_id, reaction_id, activity=None)[source]

Fetches and returns a reaction.

Parameters:
  • activity_user_id (str) – id of the user who posted the original activity

  • activity_id (str) – activity id

  • reaction_user_id (str) – id of the user who reacted

  • reaction_id (str) – id of the reaction. one of: love, wow, haha, sad, angry, thankful, pride, care

  • activity (dict) – activity object (optional)

Return type:

dict

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a new post, comment, like, or RSVP.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

contents will be a dict with id and url keys for the newly created Facebook object

Return type:

CreationResult or None

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Previews creating a new post, comment, like, or RSVP.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

contents will be an HTML snippet

Return type:

CreationResult or None

create_notification(user_id, text, link)[source]

Sends the authenticated user a notification.

Uses the Notifications API (beta): https://developers.facebook.com/docs/games/notifications/#impl

Parameters:
Raises:

urllib3.HTPPError

post_url(post)[source]

Returns a short Facebook URL for a post.

Parameters:

post (dict) – Facebook post

comment_url(post_id, comment_id, post_author_id=None)[source]

Returns a short Facebook URL for a comment.

Parameters:
  • post_id (str) – Facebook post id

  • comment_id (str) – Facebook comment id

classmethod base_id(url)[source]

Guesses the id of the object in the given URL.

Return type:

str or None

base_object(obj, verb=None, resolve_numeric_id=False)[source]

Returns the ‘base’ silo object that an object operates on.

This is mostly a big bag of heuristics for reverse engineering and parsing Facebook URLs. Whee.

Parameters:
  • obj (dict) – ActivityStreams object

  • verb (str) – optional

  • resolve_numeric_id (bool) – if True, tries harder to populate the numeric_id field by making an additional API call to look up the object if necessary.

Returns:

minimal ActivityStreams object. Usually has at least id, numeric_id, and url fields; may also have author.

Return type:

dict

post_to_activity(post)[source]

Converts a post to an activity.

Parameters:

post (dict) – a decoded JSON post

Returns:

ActivityStreams activity

Return type:

dict

post_to_object(post, type=None)[source]

Converts a post to an object.

TODO: handle the sharedposts field

Parameters:
  • post (dict) – a decoded JSON post

  • type (str) – object type: None, post, or comment

Returns:

ActivityStreams object

Return type:

dict

comment_to_object(comment, post_id=None, post_author_id=None)[source]

Converts a comment to an object.

Parameters:
  • comment (dict) – a decoded JSON comment

  • post_id (str) – optional Facebook post id. Only used if the comment id doesn’t have an embedded post id.

  • post_author_id (str) – optional Facebook post author id. Only used if the comment id doesn’t have an embedded post author id.

Returns:

ActivityStreams object

Return type:

dict

share_to_object(share)[source]

Converts a share (from /OBJECT/sharedposts) to an object.

Parameters:

share (dict) – JSON share

Returns:

ActivityStreams object

Return type:

dict

user_to_actor(user)[source]

Converts a user or page to an actor.

Parameters:

user (dict) – Facebook user or page object

Returns:

ActivityStreams actor

Return type:

dict

event_to_object(event, rsvps=None)[source]

Converts an event to an object.

Parameters:
  • event (dict) – Facebook event object

  • rsvps (sequence of dict) – optional Facebook RSVPs

Returns:

ActivityStreams object

Return type:

dict

event_to_activity(event, rsvps=None)[source]

Converts a event to an activity.

Parameters:
  • event (dict) – Facebook event object

  • rsvps (list of dict) – Facebook RSVP objects

Returns:

ActivityStreams activity

Return type:

dict

rsvp_to_object(rsvp, type=None, event=None)[source]

Converts an RSVP to an object.

The id field will ony be filled in if event['id'] is provided.

Parameters:
  • rsvp (dict) – Facebook RSVP object

  • type (str) – optional Facebook RSVP type, one of RSVP_FIELDS

  • event (dict) – Facebook event object. May contain only a single ``id` element.

Returns:

ActivityStreams object

Return type:

dict

album_to_object(album)[source]

Converts a photo album to an object.

Parameters:

album (dict) – Facebook album object

Returns:

ActivityStreams object

Return type:

dict

privacy_to_to(obj, type=None)[source]

Converts a Facebook privacy field to an ActivityStreams to field.

privacy is sometimes an object: https://developers.facebook.com/docs/graph-api/reference/post#fields

…and other times a string: https://developers.facebook.com/docs/graph-api/reference/album/#readfields

Parameters:
  • obj (dict) – Facebook object (post, album, comment, etc)

  • type (str) – object type: None, post, or comment

Returns:

ActivityStreams to object, or None if unknown

Return type:

dict

email_to_object(html)[source]

Converts a Facebook HTML notification email to an AS1 object.

Parameters:

html – str

Returns:

ActivityStreams object, or None if html couldn’t be parsed

Return type:

dict

scraped_to_activities(scraped, log_html=False, **kwargs)[source]

Converts HTML from an mbasic.facebook.com timeline to AS1 activities.

Parameters:
  • scraped (str) – HTML

  • log_html (bool)

  • kwargs – unused

Returns:

([AS activities], AS logged in actor (ie viewer))

Return type:

tuple

scraped_to_activity(scraped, log_html=False, **kwargs)[source]

Converts HTML from an mbasic.facebook.com post page to an AS1 activity.

Parameters:
  • scraped (str) – HTML from an mbasic.facebook.com post permalink

  • log_html (bool)

  • kwargs – unused

Returns:

(dict AS activity or None, AS logged in actor (ie viewer))

Return type:

tuple

merge_scraped_reactions(scraped, activity)[source]

Converts and merges scraped likes and reactions into an activity.

New likes and emoji reactions are added to the activity in tags. Existing likes and emoji reactions in tags are ignored.

Parameters:
  • scraped (str) – HTML from an mbasic.facebook.com/ufi/reaction/profile/browser/ page

  • activity (dict) – AS activity to merge these reactions into

Returns:

AS like/react tag objects converted from scraped

Return type:

list of dict

scraped_to_actor(scraped)[source]

Converts HTML from a profile about page to an AS1 actor.

Parameters:

scraped (str) – HTML from an mbasic.facebook.com post permalink

Returns:

AS1 actor

Return type:

dict

static parse_id(id, is_comment=False)[source]

Parses a Facebook post or comment id.

Facebook ids come in different formats:

  • Simple number, usually a user or post: 12

  • Two numbers with underscore, usually POST_COMMENT or USER_POST: 12_34

  • Three numbers with underscores, USER_POST_COMMENT: 12_34_56

  • Three numbers with colons, USER:POST:SHARD: 12:34:63 (We’re guessing that the third part is a shard in some FB internal system. In our experience so far, it’s always either 63 or the app-scoped user id for 63.)

  • Two numbers with colon, POST:SHARD: 12:34 (We’ve seen 0 as shard in this format.)

  • Four numbers with colons/underscore, USER:POST:SHARD_COMMENT: 12:34:63_56

  • Five numbers with colons/underscore, USER:EVENT:UNKNOWN:UNKNOWN_UNKNOWN. Not currently supported! Examples:

    • 111599105530674:998145346924699:10102446236688861:10207188792305341_998153510257216

    • 111599105530674:195181727490727:10102446236688861:10205257726909910_195198790822354

Background:

Parameters:
Returns:

Some or all fields may be None.

Return type:

FacebookId

resolve_object_id(user_id, post_id, activity=None)[source]

Resolve a post id to its Facebook object id, if any.

Used for photo posts, since Facebook has (at least) two different objects (and ids) for them, one for the post and one for each photo.

This is the same logic that we do for canonicalizing photo objects in get_activities() above.

If activity is not provided, fetches the post from Facebook.

Parameters:
  • user_id (str) – Facebook user id who posted the post

  • post_id (str) – Facebook post id

  • activity (dict) – optional AS activity representation of Facebook post

Returns:

Facebook object id or None

Return type:

str

urlopen(url, _as=<class 'dict'>, **kwargs)[source]

Wraps urllib2.urlopen() and passes through the access token.

Parameters:

_as (type) – if not None, parses the response as JSON and passes it through _as() with this type. if None, returns the response object.

Returns:

decoded JSON response

Return type:

dict

urlopen_batch(urls)[source]

Sends a batch of multiple API calls using Facebook’s batch API.

Raises the appropriate urllib2.HTTPError if any individual call returns HTTP status code 4xx or 5xx.

https://developers.facebook.com/docs/graph-api/making-multiple-requests

Parameters:

urls (sequence of str) – relative API URLs, eg [me, me/accounts]

Returns:

responses, either decoded JSON objects (when possible) or raw str bodies

Return type:

sequence of dict

urlopen_batch_full(requests)[source]

Sends a batch of multiple API calls using Facebook’s batch API.

Similar to urlopen_batch(), but the requests arg and return value are dicts with headers, HTTP status code, etc. Raises urllib2.HTTPError if the outer batch request itself returns an HTTP error.

https://developers.facebook.com/docs/graph-api/making-multiple-requests

Parameters:

requests (sequence of dict) –

requests in Facebook’s batch format, except that headers is a single dict, not a list of dicts, e.g.:

[{'relative_url': 'me/feed',
  'headers': {'ETag': 'xyz', ...},
 },
 ...
]

Returns:

responses in Facebook’s batch format, except that body is JSON-decoded if possible, and headers is a single dict, not a list of dicts, e.g.:

[{'code': 200,
  'headers': {'ETag': 'xyz', ...},
  'body': {...},
 },
 ...
]

Return type:

sequence of dict

flickr

Flickr source class.

Uses Flickr’s REST API https://www.flickr.com/services/api/

TODO: Fetching feeds with comments and/or favorites is very request intensive right now. It would be ideal to find a way to batch requests, make requests asynchronously, or make better calls to the API itself. Maybe use flickr.activity.userPhotos (https://www.flickr.com/services/api/flickr.activity.userPhotos.html) when group_id=SELF`.

class granary.flickr.Flickr(access_token_key, access_token_secret, user_id=None, path_alias=None)[source]

Bases: Source

Flickr source class. See file docstring and Source class for details.

__init__(access_token_key, access_token_secret, user_id=None, path_alias=None)[source]

Constructor.

If they are not provided, user_id and path_alias will be looked up via the API on first use.

Parameters:
  • access_token_key (str) – OAuth access token key

  • access_token_secret (str) – OAuth access token secret

  • user_id (str) – the logged in user’s Flickr nsid. (optional)

  • path_alias (str) – the logged in user’s path_alias, replaces user_id in canonical profile and photo urls (optional)

call_api_method(method, params=None)[source]

Call a Flickr API method.

upload(params, file)[source]

Upload a photo or video via the Flickr API.

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a photo, comment, or favorite.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

content will be a dict with id, url, and type keys (all optional) for the newly created Flickr object

Return type:

CreationResult or None

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Preview creation of a photo, comment, or favorite.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

whose description will be an HTML summary of what publishing will do, and whose content will be an HTML preview of the result

Return type:

CreationResult or None

delete(id)[source]

Deletes a photo. The authenticated user must have created it.

Parameters:

id (int or str) – photo id to delete

Returns:

content is Flickr API response dict

Return type:

CreationResult

preview_delete(id)[source]

Previews deleting a photo.

Parameters:

id (int or str) – photo id to delete

Return type:

CreationResult

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, start_index=0, count=0, etag=None, min_id=None, cache=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, fetch_events=False, fetch_mentions=False, search_query=None, **kwargs)[source]

Fetches Flickr photos and converts them to ActivityStreams activities.

See Source.get_activities_response() for details.

Mentions are not fetched or included because they don’t exist in Flickr. https://github.com/snarfed/bridgy/issues/523#issuecomment-155523875

get_actor(user_id=None)[source]

Get an ActivityStreams object of type person given a user’s nsid. If no user_id is provided, this method will make another API request to find out the currently logged in user’s id.

Parameters:

user_id (str) – optional

Returns:

an ActivityStreams object

Return type:

dict

user_to_actor(resp)[source]

Convert a Flickr user dict into an ActivityStreams actor.

get_comment(comment_id, activity_id=None, activity_author_id=None, activity=None)[source]

Returns an ActivityStreams comment object.

Parameters:
  • comment_id (str)

  • activity_id (str)

  • activity_author_id (str)

  • activity (dict) – activity object, optional. Avoids fetching the activity if provided.

photo_to_activity(photo)[source]

Convert a Flickr photo to an ActivityStreams object.

Takes either data in the expanded form returned by flickr.photos.getInfo or the abbreviated form returned by flickr.people.getPhotos.

Parameters:

photo (dict) – response from Flickr

Returns:

ActivityStreams object

Return type:

dict

like_to_object(person, photo_activity)[source]

Convert a Flickr favorite into an ActivityStreams like tag.

Parameters:
  • person (dict) – the person object from Flickr

  • photo_activity (dict) – the ActivityStreams object representing the photo this like belongs to

Returns:

ActivityStreams object

Return type:

dict

comment_to_object(comment, photo_id)[source]

Convert a Flickr comment JSON object to an ActivityStreams comment.

Parameters:
  • comment (dict) – the comment object from Flickr

  • photo_id (str) – the Flickr ID of the photo that this comment belongs to

Returns:

ActivityStreams object

Return type:

dict

get_user_image(farm, server, author)[source]

Convert fields from a typical Flickr response into the buddy icon URL.

https://www.flickr.com/services/api/misc.buddyicons.html

user_id()[source]

Get the nsid of the currently authorized user.

The first time this is called, it will invoke the flickr.people.getLimits API method.

https://www.flickr.com/services/api/flickr.people.getLimits.html

Return type:

str

path_alias()[source]

Get the path_alias of the currently authorized user.

The first time this is called, it will invoke the flickr.people.getInfo API method.

https://www.flickr.com/services/api/flickr.people.getInfo.html

Return type:

str

user_url(user_id)[source]

Convert a user’s path_alias to their Flickr profile page URL.

Parameters:

user_id (str) – user’s alphanumeric nsid or path alias

Returns:

a profile URL

Return type:

str

photo_url(user_id, photo_id)[source]

Construct a url for a photo.

Parameters:
  • user_id (str) – alphanumeric user ID or path alias

  • photo_id (str) – numeric photo ID

Returns:

photo URL

Return type:

str

classmethod base_id(url)[source]

Used when publishing comments or favorites.

Flickr photo ID is the 3rd path component rather than the first.

github

GitHub source class. Uses the v4 GraphQL API and the v3 REST API.

API docs:

class granary.github.GitHub(access_token=None)[source]

Bases: Source

GitHub source class. See file docstring and Source for details.

access_token

optional, OAuth access token

Type:

str

__init__(access_token=None)[source]

Constructor.

Parameters:

access_token (str) – optional OAuth access token

user_url(username)[source]

Returns the URL for a user’s profile.

classmethod base_id(url)[source]

Extracts and returns a USERNAME:REPO:ID id for an issue or PR.

Parameters:

url (str)

Return type:

str or None

graphql(graphql, kwargs)[source]

Makes a v4 GraphQL API call.

Parameters:

graphql (str) – GraphQL operation

Returns:

parsed JSON response

Return type:

dict

rest(url, data=None, parse_json=True, **kwargs)[source]

Makes a v3 REST API call.

Uses HTTP POST if data is provided, otherwise GET.

Parameters:
  • data (dict) – JSON payload for POST requests

  • json (bool) – whether to parse the response body as JSON and return it as a dict. If False, returns a requests.Response instead.

Returns:

decoded from JSON response if json=True, otherwise

requests.Response

Return type:

dict

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, start_index=0, count=0, etag=None, min_id=None, cache=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, fetch_events=False, fetch_mentions=False, search_query=None, public_only=True, **kwargs)[source]

Fetches issues and comments and converts them to ActivityStreams activities.

See Source.get_activities_response() for details.

Not comprehensive! Uses the notifications API (v3 REST).

Also note that start_index is not currently supported.

fetch_likes determines whether emoji reactions are fetched: https://help.github.com/articles/about-conversations-on-github#reacting-to-ideas-in-comments

The notifications API call supports Last-Modified/If-Modified-Since headers and 304 Not Changed responses. If provided, etag should be an RFC2822 timestamp, usually the exact value returned in a Last-Modified header. It will also be passed to the comments API endpoint as the since= value (converted to ISO8601).

get_actor(user_id=None)[source]

Fetches and returns a user.

Parameters:

user_id (str) – defaults to current user

Returns:

ActivityStreams actor object

Return type:

dict

get_comment(comment_id, **kwargs)[source]

Fetches and returns a comment.

Parameters:

comment_id (str) – comment id (either REST or GraphQL), of the form REPO-OWNER:REPO-NAME:ID, e.g. snarfed:bridgy:456789

Returns:

an ActivityStreams comment object

Return type:

dict

render_markdown(markdown, owner, repo)[source]

Uses the GitHub API to render GitHub-flavored Markdown to HTML.

Parameters:
  • markdown (str) – input

  • repo (owner and) – the repo to render in, for context. affects git hashes #XXX for issues/PRs.

Returns:

rendered HTML

Return type:

str

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a new issue or comment.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

contents will be a dict with id and url keys for the newly created GitHub object

Return type:

CreationResult or None

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Previews creating an issue or comment.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

contents will be a str HTML snippet

Return type:

CreationResult or None

existing_labels(owner, repo)[source]

Fetches and returns a repo’s labels.

Parameters:
  • owner (str) – GitHub username or org that owns the repo

  • repo (str)

Return type:

set of str

issue_to_object(issue)[source]

Converts a GitHub issue or pull request to ActivityStreams.

Handles both v4 GraphQL and v3 REST API issue and PR objects.

Parameters:

issue (dict) – GitHub issue or PR

Returns:

ActivityStreams object

Return type:

dict

pr_to_object(issue)

Converts a GitHub issue or pull request to ActivityStreams.

Handles both v4 GraphQL and v3 REST API issue and PR objects.

Parameters:

issue (dict) – GitHub issue or PR

Returns:

ActivityStreams object

Return type:

dict

comment_to_object(comment)[source]

Converts a GitHub comment to ActivityStreams.

Handles both v4 GraphQL and v3 REST API issue objects.

Parameters:

comment (dict) – GitHub issue

Returns:

ActivityStreams comment

Return type:

dict

reaction_to_object(reaction, target)[source]

Converts a GitHub emoji reaction to ActivityStreams.

Handles v3 REST API reaction objects.

https://developer.github.com/v3/reactions/

Parameters:
  • reaction (dict) – v3 GitHub reaction

  • target (dict) – ActivityStreams object of reaction

Returns:

ActivityStreams reaction

Return type:

dict

user_to_actor(user)[source]

Converts a GitHub user to an ActivityStreams actor.

Handles both v4 GraphQL and v3 REST API user objects.

Parameters:

user (dict) – GitHub user

Returns:

ActivityStreams actor

Return type:

dict

instagram

Instagram source class.

Instagram’s API doesn’t tell you if a user has marked their account private or not, so the Audience Targeting to field is currently always set to @public.

class granary.instagram.Instagram(access_token=None, allow_comment_creation=False, scrape=False, cookie=None)[source]

Bases: Source

Instagram source class. See file docstring and Source class for details.

__init__(access_token=None, allow_comment_creation=False, scrape=False, cookie=None)[source]

Constructor.

If an OAuth access token is provided, it will be passed on to Instagram. This will be necessary for some people and contact details, based on their privacy settings.

Parameters:
  • access_token (str) – optional OAuth access token

  • allow_comment_creation (bool) – optionally disable comment creation, useful if the app is not approved to create comments.

  • scrape (bool) – whether to scrape instagram.com’s HTML (True) or use the API (False)

  • cookie (str) – optional sessionid cookie to use when scraping.

urlopen(url, **kwargs)[source]

Wraps urllib2.urlopen() and passes through the access token.

classmethod user_url(username)[source]

Returns the URL for a user’s profile.

get_actor(user_id=None, **kwargs)[source]

Returns a user as a JSON ActivityStreams actor dict.

Parameters:
  • user_id (str) – id or username. Defaults to self, ie the current user.

  • kwargs – if scraping, passed through to :meth:get_activities_response``.

Raises:

AssertionError – if kwargs is provided but we’re not scraping

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, start_index=0, count=0, etag=None, min_id=None, cache=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, fetch_events=False, fetch_mentions=False, search_query=None, scrape=False, cookie=None, ignore_rate_limit=False, **kwargs)[source]

Fetches posts and converts them to ActivityStreams activities.

See Source.get_activities_response() for details. app_id is ignored. Supports min_id, but not ETag, since Instagram doesn’t support it.

Likes are always included, regardless of the fetch_likes kwarg. They come bundled in the likes field of the API Media object: http://instagram.com/developer/endpoints/media/#

Mentions are never fetched or included because the API doesn’t support searching for them. https://github.com/snarfed/bridgy/issues/523#issuecomment-155523875

Shares are never fetched or included since there is no share feature.

Instagram only supports search over hashtags, so if search_query is set, it must begin with #.

May populate a custom ig_like_count property in media objects. (Currently only when scraping.)

Parameters:
  • scrape (bool) – if True, scrapes HTML from instagram.com instead of using the API. Populates the user’s actor object in the actor response field. Useful for apps that haven’t yet been approved in the new permissions approval process. Currently only supports group_id=SELF. Also supports passing a shortcode as activity_id as well as the internal API id. http://developers.instagram.com/post/133424514006/instagram-platform-update

  • cookie (str) – only used if scrape=True

  • ignore_rate_limit (bool) – for scraping, always make an HTTP request, even if we’ve been rate limited recently

get_comment(comment_id, activity_id=None, activity_author_id=None, activity=None)[source]

Returns an ActivityStreams comment object.

Parameters:
  • comment_id (str) – comment id

  • activity_id (str) – activity id, optional

  • activity_author_id (str) – activity author id. Ignored.

  • activity (dict) – activity object, optional. Avoids fetching the activity.

get_share(activity_user_id, activity_id, share_id, activity=None)[source]

Not implemented. Returns None. Resharing isn’t a feature of Instagram.

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a new comment or like.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

if successful, content will have and id and url

keys for the newly created Instagram object

Return type:

CreationResult

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Preview a new comment or like.

Parameters:
  • obj (Dcit) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

if successful, content and description will describe the

new Instagram object.

Return type:

CreationResult

media_to_activity(media)[source]

Converts a media to an activity.

http://instagram.com/developer/endpoints/media/#get_media

Parameters:

media (dict) – JSON object retrieved from the Instagram API

Returns:

ActivityStreams activity

Return type:

dict

media_to_object(media)[source]

Converts a media to an object.

Parameters:

media (dict) – JSON object retrieved from the Instagram API

Returns:

ActivityStreams object

Return type:

dict

comment_to_object(comment, media_id, media_url)[source]

Converts a comment to an object.

Parameters:
  • comment (dict) – JSON object retrieved from the Instagram API

  • media_id (str)

  • media_url (str)

Returns:

ActivityStreams object

Return type:

dict

like_to_object(liker, media_id, media_url)[source]

Converts a like to an object.

Parameters:
  • liker (dict) – JSON object from the Instagram API, user who does the liking

  • media_id (str)

  • media_url (str)

Returns:

ActivityStreams object

Return type:

dict

user_to_actor(user)[source]

Converts a user to an actor.

Parameters:

user (dict) – JSON object from the Instagram API

Returns:

ActivityStreams actor

Return type:

dict

base_object(obj)[source]

Extends the default base_object() to avoid using shortcodes as object ids.

static id_to_shortcode(id)[source]

Converts a media id to the shortcode used in its instagram.com URL.

Based on http://carrot.is/coding/instagram-ids , which determined that shortcodes are just URL-safe base64 encoded ids.

scraped_to_activities(input, cookie=None, count=None, fetch_extras=False)[source]

Converts scraped Instagram HTML to ActivityStreams activities.

The input HTML may be from:

Parameters:
  • input (str) – containing either HTML or JSON

  • cookie (str) – optional sessionid cookie to be used for subsequent HTTP fetches, if necessary.

  • count (int) – number of activities to return, None for all

  • fetch_extras (bool) – whether to make extra HTTP fetches to get likes, etc.

Returns:

([ActivityStreams activities], ActivityStreams viewer actor)

Return type:

tuple

scraped_json_to_activities(input, cookie=None, count=None, fetch_extras=False)[source]

Converts scraped Instagram JSON to ActivityStreams activities.

The input JSON may be from a user’s profile, eg

https://i.instagram.com/api/v1/users/web_profile_info/?username=

Parameters:
  • input (dict or sequence of dicts) – Instagram JSON object(s)

  • cookie (str) – optional sessionid cookie to be used for subsequent HTTP fetches, if necessary.

  • count (int) – number of activities to return, None for all

  • fetch_extras (bool) – whether to make extra HTTP fetches to get likes, etc.

Returns:

([ActivityStreams activities], ActivityStreams viewer actor)

Return type:

tuple

html_to_activities(input, cookie=None, count=None, fetch_extras=False)

Converts scraped Instagram HTML to ActivityStreams activities.

The input HTML may be from:

Parameters:
  • input (str) – containing either HTML or JSON

  • cookie (str) – optional sessionid cookie to be used for subsequent HTTP fetches, if necessary.

  • count (int) – number of activities to return, None for all

  • fetch_extras (bool) – whether to make extra HTTP fetches to get likes, etc.

Returns:

([ActivityStreams activities], ActivityStreams viewer actor)

Return type:

tuple

scraped_to_activity(html, **kwargs)[source]

Converts HTML from photo/video permalink page to an AS1 activity.

Parameters:
  • html (str) – HTML from a photo/video page on instagram.com

  • kwargs – passed through to scraped_to_activities

Returns:

(AS activity or None, AS logged in actor (ie viewer))

Return type:

tuple

scraped_to_actor(html, **kwargs)[source]

Extracts and returns the logged in actor from any Instagram HTML.

Parameters:

html (str)

Returns:

AS1 actor

Return type:

dict

merge_scraped_comments(scraped, activity)[source]

Converts and merges scraped comments (replies) into an activity.

Parameters:
  • scraped (str or sequence) – scraped JSON comments

  • activity (dict) – AS activity to merge these comments into

Returns:

AS comment objects converted from scraped

Return type:

list of dict

Raises:

ValueError – if scraped is not valid JSON

merge_scraped_reactions(scraped, activity)[source]

Converts and merges scraped likes and reactions into an activity.

New likes and emoji reactions are added to the activity in tags. Existing likes and emoji reactions in tags are ignored.

Supports both legacy and v2 Instagram JSON.

Parameters:
  • scraped (str or dict) – scraped JSON likes

  • activity (dict) – AS activity to merge these reactions into

Returns:

AS like tag objects converted from scraped

Return type:

list of dict

Raises:

ValueError – if scraped is not valid JSON

jsonfeed

Convert between ActivityStreams and JSON Feed.

JSON Feed spec: https://jsonfeed.org/version/1.1

granary.jsonfeed.activities_to_jsonfeed(activities, actor=None, title=None, feed_url=None, home_page_url=None)[source]

Converts ActivityStreams activities to a JSON feed.

Parameters:
  • activities (sequence of dict) – ActivityStreams activities

  • actor (dict) – ActivityStreams actor, the author of the feed

  • title (str) – the feed title

  • home_page_url (str) – the home page URL

  • feed_url (str) – the URL of the JSON Feed, if any. Included in the feed_url field.

Returns:

JSON Feed data

Return type:

dict

granary.jsonfeed.jsonfeed_to_activities(jsonfeed)[source]

Converts a JSON feed to ActivityStreams activities and actor.

Parameters:

jsonfeed (dict) – JSON Feed data

Returns:

(activities, actor), where activities and actor are both ActivityStreams object dicts

Return type:

tuple

Raises:

ValueError – if jsonfeed isn’t a valid JSON Feed dict

mastodon

Mastodon source class.

Mastodon is an ActivityPub implementation, but it also has a REST + OAuth 2 API independent of AP. This class handles that API. API docs: https://docs.joinmastodon.org/api/

May also be used for services with Mastodon-compatible APIs, eg Pleroma: https://docs-develop.pleroma.social/backend/API/differences_in_mastoapi_responses/

class granary.mastodon.Mastodon(instance, access_token, user_id=None, truncate_text_length=None)[source]

Bases: Source

Mastodon source class. See file docstring and Source class for details.

instance

base URL of Mastodon instance, eg https://mastodon.social/

Type:

str

user_id

optional, current user’s id (not username!) on this instance

Type:

int

access_token

optional, OAuth access token

Type:

str

__init__(instance, access_token, user_id=None, truncate_text_length=None)[source]

Constructor.

If user_id is not provided, it will be fetched via the API.

Parameters:
  • instance (str) – base URL of Mastodon instance, eg https://mastodon.social/

  • user_id – (str or int): optional, current user’s id (not username!) on this instance

  • access_token (str) – optional OAuth access token

  • truncate_text_length (int) – optional character limit for toots, overrides the default of 500

user_url(username)[source]

Returns the URL for a user’s profile.

classmethod embed_post(obj)[source]

Returns the HTML for embedding a toot.

https://docs.joinmastodon.org/methods/oembed/

Parameters:

obj (dict) – AS1 object with at least url, and optionally also content.

Returns:

HTML

Return type:

str

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, include_shares=True, fetch_events=False, fetch_mentions=False, search_query=None, start_index=0, count=0, cache=None, **kwargs)[source]

Fetches toots and converts them to ActivityStreams activities.

See Source.get_activities_response() for details.

get_actor(user_id=None)[source]

Fetches and returns an account.

Parameters:

user_id (str) – defaults to current account

Returns:

ActivityStreams actor object

Return type:

dict

get_comment(comment_id, **kwargs)[source]

Fetches and returns a comment.

Parameters:
  • comment_id (str) – status id

  • kwargs – unused

Returns:

ActivityStreams object

Return type:

dict

Raises:

ValueError – if comment_id is invalid

status_to_activity(status)[source]

Converts a status to an activity.

Parameters:

status (dict) – a decoded JSON status

Returns:

ActivityStreams activity

Return type:

dict

status_to_object(status)[source]

Converts a status to an object.

Parameters:

status (dict) – a decoded JSON status

Returns:

ActivityStreams object

Return type:

dict

user_to_actor(account)[source]

Converts a Mastodon account to an AS1 actor.

Parameters:

account (dict) – Mastodon account

Returns:

AS1 actor

Return type:

dict

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a status (aka toot), reply, boost (aka reblog), or favorite.

https://docs.joinmastodon.org/methods/statuses/

Parameters:
  • obj – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

whose content will be a dict with id, url, and type keys (all optional) for the newly created object (or None)

Return type:

CreationResult

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Preview creating a status (aka toot), reply, boost (aka reblog), or favorite.

https://docs.joinmastodon.org/methods/statuses/

Parameters:
  • obj – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

content will be a str HTML snippet or None

Return type:

CreationResult

base_object(obj)[source]

Returns the “base” Mastodon object that an object operates on.

If the object is a reply, boost, or favorite of a Mastodon post - on any instance - this returns that post object. The id in the returned object is the id of that remote post on the local instance. (As a Mastodon style id, ie an int in a string, not a tag URI.)

Uses Mastodon’s search API on the local instance to determine whether a URL is a Mastodon post, and if it is, to find or generate an id for it on the local instance.

Discovered via https://mastodon.social/@jkreeftmeijer/101245063526942536

Parameters:

obj (dict) – ActivityStreams object

Returns:

minimal ActivityStreams object. Usually has at least id; may also have url, author, etc.

Return type:

dict

status_url(id)[source]

Returns the local instance URL for a status with a given id.

upload_media(media)[source]

Uploads one or more images or videos from web URLs.

Parameters:

media (sequence of dict) – AS image or stream objects, eg: [{'url': 'http://picture', 'displayName': 'a thing'}, ...]

Returns:

media ids for uploaded files

Return type:

list of str

delete(id)[source]

Deletes a toot. The authenticated user must have authored it.

Parameters:

id (int or str) – toot id (on local instance) to delete

Returns:

content is dict with url and id fields

Return type:

CreationResult

preview_delete(id)[source]

Previews deleting a toot.

Parameters:

id (int or str) – toot id (on local instance) to delete

Return type:

CreationResult

get_blocklist_ids()[source]

Returns the current user’s block list as a list of int account ids.

May make multiple API calls to fully fetch large block lists. https://docs.joinmastodon.org/methods/accounts/blocks/

Returns:

Mastodon account ids on the current instance

Return type:

sequence of int

meetup

Meetup.com source class.

class granary.meetup.Meetup(access_token)[source]

Bases: Source

classmethod embed_post(obj)[source]

Returns the HTML string for embedding an RSVP from Meetup.com.

Parameters:

obj (dict) – AS1 object with at least url, and optionally also content.

Returns:

HTML

Return type:

str

__init__(access_token)[source]
create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a new object: a post, comment, like, share, or RSVP.

Subclasses should override this. Different sites will support different functionality; check each subclass for details. The actor will usually be the authenticated user.

Parameters:
  • obj (dict) – ActivityStreams object. At minimum, must have the content field. objectType is strongly recommended.

  • include_link (str) – INCLUDE_LINK, OMIT_LINK, or INCLUDE_IF_TRUNCATED; whether to include a link to the object (if it has one) in the content.

  • ignore_formatting (bool) – whether to use content text as is, instead of converting its HTML to plain text styling (newlines, etc.)

Returns:

The result. content will be a dict or None. If the newly created object has an id or permalink, they’ll be provided in the values for id and url.

Return type:

CreationResult

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Previews creating a new object: a post, comment, like, share, or RSVP.

Returns HTML that previews what create() with the same object will do.

Subclasses should override this. Different sites will support different functionality; check each subclass for details. The actor will usually be the authenticated user.

Parameters:
  • obj (dict) – ActivityStreams object. At minimum, must have the content field. objectType is strongly recommended.

  • include_link (str) – INCLUDE_LINK, OMIT_LINK, or INCLUDE_IF_TRUNCATED; whether to include a link to the object (if it has one) in the content.

  • ignore_formatting (bool) – whether to use content text as is, instead of converting its HTML to plain text styling (newlines, etc.)

Returns:

The result. content will be a dict or None.

Return type:

CreationResult

user_to_actor(user)[source]

Converts a user to an actor.

Parameters:

user (dict) – a decoded JSON Meetup user

Returns:

ActivityStreams actor

Return type:

dict

user_url(user_id)[source]

Returns the URL for a user’s profile.

microformats2

Convert ActivityStreams to microformats2 HTML and JSON.

Microformats2 specs: http://microformats.org/wiki/microformats2

ActivityStreams 1 specs: http://activitystrea.ms/specs/

granary.microformats2.get_string_urls(objs)[source]

Extracts string URLs from a list of either string URLs or mf2 dicts.

Many mf2 properties can contain either string URLs or full mf2 objects, eg h-cite. in-reply-to is the most commonly used example: http://indiewebcamp.com/in-reply-to#How_to_consume_in-reply-to

Parameters:

objs (sequence of str or dict) – URLs or embedded mf2 objects

Returns:

URLs

Return type:

list of str

granary.microformats2.get_html(val)[source]

Returns a string value that may have HTML markup.

Parameters:

value (str or dict) – mf2 property value, either str or {'html': '<p>str</p>', 'value': 'str'}

Return type:

str or None

granary.microformats2.get_text(val)[source]

Returns a plain text string value. See get_html().

granary.microformats2.maybe_normalize_iso8601(val)[source]

Tries to normalize a string datetime value to ISO-8601.

Parameters:

val (str)

Returns:

normalized ISO-8601 if val can be parsed, otherwise val unchanged

Return type:

str

granary.microformats2.activity_to_json(activity, **kwargs)[source]

Converts an ActivityStreams activity to microformats2 JSON.

Parameters:
Returns:

decoded microformats2 JSON

Return type:

dict

granary.microformats2.object_to_json(obj, trim_nulls=True, entry_class='h-entry', default_object_type=None, synthesize_content=True)[source]

Converts an ActivityStreams object to microformats2 JSON.

Parameters:
  • obj (dict) – a decoded JSON ActivityStreams object

  • trim_nulls (bool) – whether to remove elements with null or empty values

  • entry_class (str or sequence of str), the mf2 class(es) – given, eg h-cite when parsing a reference to a foreign entry. defaults to h-entry`

  • default_object_type (str) – the ActivityStreams objectType to use if one is not present. defaults to None

  • synthesize_content (bool) – whether to generate synthetic content if the object doesn’t have its own, eg likes this or shared this

Returns:

decoded microformats2 JSON

Return type:

dict

granary.microformats2.json_to_object(mf2, actor=None, fetch_mf2=False, rel_urls=None)[source]

Converts a single microformats2 JSON item to an ActivityStreams object.

Supports h-entry, h-event, h-card, and other single item times. Does not yet support h-feed.

If rel_urls is provided, the returned url and urls fields will be objects that may include displayName fields with the text or title from the original HTML links.

Parameters:
  • mf2 (dict) – decoded JSON microformats2 object

  • actor (dict) – optional author AS actor object. usually comes from a rel="author" link. if mf2 has its own author, that overrides this

  • fetch_mf2 (bool) – whether to fetch additional pages via HTTP if necessary, eg to determine authorship: https://indieweb.org/authorship

  • rel_urls (dict) – optional rel-urls field from parsed mf2

Returns:

ActivityStreams object

Return type:

dict

granary.microformats2.html_to_activities(html, url=None, actor=None, id=None)[source]

Converts a microformats2 HTML h-feed to ActivityStreams activities.

Parameters:
  • html (str) – HTML or requests.Response

  • url (str) – optional URL that HTML came from

  • actor (dict) – optional author AS actor object for all activities. usually comes from a rel="author" link.

  • id (str) – optional id of specific element to extract and parse. defaults to the whole page.

Returns:

ActivityStreams activities

Return type:

list of dict

granary.microformats2.json_to_activities(parsed, actor=None)[source]

Converts parsed microformats2 JSON to ActivityStreams activities.

Parameters:
  • parsed (dict) – parsed JSON microformats2 object

  • actor (dict) – optional author AS actor object for all activities. usually comes from a rel="author" link.

Returns:

ActivityStreams activities

Return type:

list of dict

granary.microformats2.activities_to_html(activities, extra='', body_class='')[source]

Converts ActivityStreams activities to a microformats2 HTML h-feed.

Parameters:
  • obj (dict) – a decoded JSON ActivityStreams object

  • extra (str) – extra HTML to be included inside the body tag, at the top

  • body_class (str) – included as the body tag’s class attribute

Returns:

the content field in obj with the tags in the tags field converted to links if they have startIndex and length, otherwise added to the end.

Return type:

str

granary.microformats2.object_to_html(obj, parent_props=None, synthesize_content=True)[source]

Converts an ActivityStreams object to microformats2 HTML.

Features:

  • linkifies embedded tags and adds links for other tags

  • linkifies embedded URLs

  • adds links, summaries, and thumbnails for attachments and checkins

  • adds a “via SOURCE” postscript

Parameters:
  • obj (dict) – a decoded JSON ActivityStreams object

  • parent_props (list of str) – the properties of the parent object where this object is embedded, eg ['u-repost-of']

  • synthesize_content (bool) – whether to generate synthetic content if the object doesn’t have its own, eg likes this or shared this

Returns:

the content field in obj with tags in the tags field converted to links if they have startIndex and length, otherwise added to the end.

Return type:

str

granary.microformats2.json_to_html(obj, parent_props=None)[source]

Converts a microformats2 JSON object to microformats2 HTML.

See object_to_html() for details.

Parameters:
  • obj (dict) – a decoded microformats2 JSON object

  • parent_props (list) – of str, the properties of the parent object where this object is embedded, eg u-repost-of

Returns:

HTML

Return type:

str

granary.microformats2.hcard_to_html(hcard, parent_props=None)[source]

Renders an h-card as HTML.

Parameters:
  • hcard (dict) – decoded JSON h-card

  • parent_props (list) – of str, the properties of the parent object where this object is embedded, eg ['p-author']

Returns:

str, rendered HTML

granary.microformats2.render_content(obj, include_location=True, synthesize_content=True, render_attachments=False, render_image=False, white_space_pre=True)[source]

Renders the content of an ActivityStreams object as HTML.

Includes tags, mentions, and non-note/article attachments. (Note/article attachments are converted to mf2 children in object_to_json and then rendered in json_to_html().)

Parameters:
  • obj (dict) – decoded JSON ActivityStreams object

  • include_location (bool) – whether to render location, if provided

  • synthesize_content (bool) – whether to generate synthetic content if the object doesn’t have its own, eg likes this or shared this

  • render_attachments (bool) – whether to render attachments, eg links, images, audio, and video

  • render_image (bool) – whether to render the object’s image(s)

  • white_space_pre (bool) – whether to wrap in CSS white-space: pre. If False, newlines will be converted to <br> tags instead. Background: https://indiewebcamp.com/note#Indieweb_whitespace_thinking

Returns:

rendered HTML

Return type:

str

granary.microformats2.find_author(parsed, **kwargs)[source]

Returns the author of a page as a ActivityStreams actor.

Parameters:
  • parsed (dict) – parsed mf2 object (ie return value from mf2py.parse())

  • kwargs – passed through to mf2util.find_author()

Returns:

ActivityStreams actor

Return type:

dict

granary.microformats2.get_title(mf2)[source]

Returns an mf2 object’s title, ie its name.

Parameters:

mf2 (dict) – parsed mf2 object (ie return value from mf2py.parse())

Returns:

title, possibly ellipsized

Return type:

str

granary.microformats2.first_props(props)[source]

Converts a multiply-valued dict to singly valued.

Parameters:

props (dict) – properties, where each value is a sequence

Returns:

corresponding dict with just the first value of each sequence, or '' if the sequence is empty

Return type:

dict

granary.microformats2.tags_to_html(tags, classname, visible=True)[source]

Returns an HTML string with links to the given tag objects.

Parameters:
  • tags (sequence of dict) – decoded JSON ActivityStreams objects

  • classname (str) – class for span to enclose tags in

  • visible (bool) – whether to visibly include displayName

Return type:

str

granary.microformats2.author_display_name(hcard)[source]

Returns a human-readable string display name for an h-card object.

granary.microformats2.maybe_linked_name(props)[source]

Returns the HTML for a p-name with an optional u-url inside.

Parameters:

props (dict) – multiply-valued properties

Returns:

HTML

Return type:

str

granary.microformats2.img(src, alt='')[source]

Returns an <img> str with the given src, class, and alt.

Parameters:
  • src (str) – URL or dict with value and (optionally) alt

  • alt (str) – alt attribute value, or None

Return type:

str

granary.microformats2.vid(src, poster='')[source]

Returns an <video> str with the given src and poster.

Parameters:
  • src (str) – URL of the video

  • poster (str) – optional URL of the poster or preview image

Return type:

str

granary.microformats2.aud(src)[source]

Returns an <audio> str with the given src.

Parameters:

src (str) – URL of the audio

Return type:

str

granary.microformats2.maybe_linked(text, url=None, linked_classname=None, unlinked_classname=None)[source]

Wraps text in an <a href=...> iff a non-empty url is provided.

Parameters:
  • text (str)

  • url (str) – optional

  • linked_classname (str) – optional class attribute to use if url

  • unlinked_classname (str) – optional class attribute to use if not url

Return type:

str

granary.microformats2.maybe_datetime(dt, classname)[source]

Returns a <time datetime=...> elem if dt is non-empty.

Parameters:
  • dt (str) – RFC339 datetime or None

  • classname (str) – class name

Return type:

str

granary.microformats2.size_to_bytes(size)[source]

Converts a string file size to an integer number of bytes.

Parameters:

size (str) – may be either int bytes or human-readable approximation, eg 7MB or 1.23 kb

Returns:

int, bytes or None if size can’t be parsed

nostr

Nostr.

NIPS implemented:

  • 01: base protocol, events, profile metadata

  • 02: contacts/followings

  • 05: domain identifiers

  • 09: deletes

  • 10: replies, mentions

  • 12: hashtags, locations

  • 14: subject tag in notes

  • 18: reposts, including 10 for e/p tags

  • 19: bech32-encoded ids

  • 21: nostr: URI scheme

  • 23: articles

  • 25: likes, emoji reactions

  • 27: text notes

  • 39: external identities

  • 50: search

TODO:

  • 05: DNS verification

  • 11: relay info (like nodeinfo)

  • 12: tag queries

  • 16, 33: ephemeral/replaceable events

  • 27: user mentions, note/event mentions

  • the difficulty is that the Nostr tags don’t include human-readable

  • text. clients are supposed to get that from their local database.

  • 32: tag activities

  • 46: “Nostr Connect,” signing proxy that holds user’s keys

  • 65: user relays. what would this be in AS1? anything?

granary.nostr.id_for(event)[source]

Generates an id for a Nostr event.

Parameters:

event (dict) – Nostr event

Returns:

32-character hex-encoded sha256 hash of the event, serialized according to NIP-01

Return type:

str

granary.nostr.uri_to_id(uri)[source]

Converts a nostr: URI with bech32-encoded id to a hex sha256 hash id.

Based on NIP-19 and NIP-21.

Parameters:

uri (str)

Return type:

str

granary.nostr.id_to_uri(prefix, id)[source]

Converts a hex sha256 hash id to a nostr: URI with bech32-encoded id.

Based on NIP-19 and NIP-21.

Parameters:
Return type:

str

granary.nostr.from_as1(obj)[source]

Converts an ActivityStreams 1 activity or object to a Nostr event.

Parameters:

obj (dict) – AS1 activity or object

Returns:

Nostr event

Return type:

dict

granary.nostr.to_as1(event)[source]

Converts a Nostr event to an ActivityStreams 2 activity or object.

Parameters:

event (dict) – Nostr event

Returns:

AS1 activity or object

Return type:

dict

class granary.nostr.Nostr(relays)[source]

Bases: Source

Nostr source class. See file docstring and Source for details.

relays

relay hostnames

Type:

sequence of str

__init__(relays)[source]

Constructor.

get_actor(user_id=None)[source]

Fetches and returns a Nostr user profile.

Parameters:

user_id (str) – NIP-21 nostr:npub...

Returns:

AS1 actor object

Return type:

dict

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, include_shares=True, fetch_events=False, fetch_mentions=False, search_query=None, start_index=None, count=None, cache=None, **kwargs)[source]

Fetches events and converts them to AS1 activities.

See Source.get_activities_response() for more information.

query(websocket, filter)[source]

Runs a Nostr REQ query on an open websocket.

Sends the query, collects the responses, and closes the REQ subscription. If limit is not set on the filter, it defaults to 20.

Parameters:
Returns:

Nostr events

Return type:

list of dict

Raises:

AssertionError – if the filter limit field is not set.

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a new object: a post, comment, like, repost, etc.

See Source.create() docstring for details.

pixelfed

Pixelfed source class, heavily based on Mastodon.

Pixelfed’s API is a clone of Mastodon’s. https://docs.pixelfed.org/technical-documentation/api-v1.html

class granary.pixelfed.Pixelfed(instance, access_token, user_id=None, truncate_text_length=None)[source]

Bases: Mastodon

Pixelfed source class.

user_url(username)[source]

Returns the URL for a user’s profile.

status_url(username, id)[source]

Returns the local instance URL for a status with a given id.

get_activities_response(*args, **kwargs)[source]

Fetches toots and converts them to ActivityStreams activities.

See Source.get_activities_response() for details.

reddit

Reddit source class.

Not thread safe!

Reddit API docs:

PRAW API docs: https://praw.readthedocs.io/

class granary.reddit.Reddit(refresh_token)[source]

Bases: Source

Reddit source class. See file docstring and source.Source for details.

__init__(refresh_token)[source]
classmethod post_id(url)[source]

Guesses the post id of the given URL.

Parameters:

url (str)

Return type:

str or None

praw_to_actor(praw_user)[source]

Converts a PRAW Redditor to an actor.

Makes external calls to fetch data from the Reddit API.

https://praw.readthedocs.io/en/latest/code_overview/models/redditor.html

Caches fetched user data for 5m to avoid repeating user profile API requests when fetching multiple comments or posts from the same author. Background: https://github.com/snarfed/bridgy/issues/1021

Ideally this would be part of PRAW, but they seem uninterested:

Parameters:

user (praw.models.Redditor)

Returns:

ActivityStreams actor

Return type:

dict

user_to_actor(user)[source]

Converts a dict user to an actor.

Parameters:

user (dict) – Reddit user

Returns:

ActivityStreams actor

Return type:

dict

praw_to_object(thing, type)[source]

Converts a PRAW object to an AS1 object.

Currently only returns public content.

Note that this will make external API calls to lazily load some attributes.

Parameters:
Returns:

ActivityStreams object

Return type:

dict

praw_to_activity(thing, type)[source]

Converts a PRAW submission or comment to an activity.

Note that this will make external API calls to lazily load some attributes.

Parameters:
Returns:

ActivityStreams activity

Return type:

dict

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, start_index=0, count=None, etag=None, min_id=None, cache=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, fetch_events=False, fetch_mentions=False, search_query=None, **kwargs)[source]

Fetches submissions and ActivityStreams activities.

Currently only implements activity_id, search_query and fetch_replies.

get_actor(user_id=None)[source]

Fetches a Reddit user and converts them to an AS1 actor.

Parameters:

user_id (str)

Returns

dict: AS1 actor, or {} if the user isn’t found

get_comment(comment_id, activity_id=None, activity_author_id=None, activity=None)[source]

Returns an ActivityStreams comment object.

Parameters:
  • comment_id (str) – comment id

  • activity_id (str) – activity id; ignored!

  • activity_author_id (str) – activity author id; ignored!

  • activity (dict) – activity object; ignored!

Returns:

ActivityStreams object

Return type:

dict

user_url(username)[source]

Returns the Reddit URL for a given user.

rss

Convert between ActivityStreams and RSS 2.0.

RSS 2.0 spec: http://www.rssboard.org/rss-specification

Feedgen docs: https://feedgen.kiesow.be/

Apple iTunes Podcasts feed requirements: https://help.apple.com/itc/podcasts_connect/#/itc1723472cb

Notably:

  • Valid RSS 2.0.

  • Each podcast item requires <guid>.

  • Images should be JPEG or PNG, 1400x1400 to 3000x3000.

  • HTTP server that hosts assets and files should support range requests.

granary.rss.from_activities(activities, actor=None, title=None, feed_url=None, home_page_url=None, hfeed=None)[source]

Converts ActivityStreams activities to an RSS 2.0 feed.

Parameters:
  • activities (sequence) – of ActivityStreams activity dicts

  • actor (dict) – ActivityStreams actor, author of the feed

  • title (str) – the feed title

  • feed_url (str) – the URL for this RSS feed

  • home_page_url (str) – the home page URL

  • hfeed (dict) – parsed mf2 h-feed, if available

Returns:

RSS 2.0 XML

Return type:

str

granary.rss.to_activities(rss)[source]

Converts an RSS feed to ActivityStreams 1 activities.

Parameters:

rss (str) – RSS document with top-level <rss> element

Returns:

ActivityStreams activity

Return type:

list of dict

source

Source base class.

Based on the OpenSocial ActivityStreams REST API: http://opensocial-resources.googlecode.com/svn/spec/2.0.1/Social-API-Server.xml#ActivityStreams-Service

Uses the to field of the Audience Targeting extension to indicate an activity’s privacy settings. It’s set to a group with alias @public or @private, or unset if unknown. http://activitystrea.ms/specs/json/targeting/1.0/#anchor3

class granary.source.CreationResult(content, description, abort, error_plain, error_html)

Bases: tuple

Result of creating a new object in a silo.

create() and preview_create() use this to provide a detailed description of publishing failures. If abort is False, we should continue looking for an entry to publish; if True, we should immediately inform the user. error_plain text is sent in response to failed publish webmentions; error_html will be displayed to the user when publishing interactively.

content

str HTML snippet for preview_create(), dict for create()

Type:

str or dict

description

HTML snippet describing the publish action, e.g. @-reply or RSVP yes to this event. The verb itself is surrounded by a <span class="verb"> to allow styling. May also include <a> link(s) and embedded silo post(s).

Type:

str

abort
Type:

bool

error_plain
Type:

str

error_html
Type:

str

exception granary.source.RateLimited(*args, **kwargs)[source]

Bases: BaseException

Raised when an API rate limits us, and we may have a partial result.

partial

the partial result, if any. Usually a list.

__init__(*args, **kwargs)[source]
granary.source.html_to_text(html, baseurl='', **kwargs)[source]

Converts HTML to plain text with html2text.

Parameters:
Returns:

converted plain text

Return type:

str

granary.source.load_json(body, url)[source]

Utility method to parse a JSON string. Raises HTTPError 502 on failure.

granary.source.creation_result(content=None, description=None, abort=False, error_plain=None, error_html=None)[source]

Creates a new CreationResult.

class granary.source.SourceMeta(name, bases, class_dict)[source]

Bases: type

Source metaclass. Registers all source classes in the sources global.

class granary.source.Source[source]

Bases: object

Abstract base class for a source (e.g. Facebook, Twitter).

Concrete subclasses must override the class constants below and implement get_activities().

DOMAIN

the source’s domain

Type:

str

BASE_URL

optional, the source’s base url

Type:

str

NAME

the source’s human-readable name

Type:

str

FRONT_PAGE_TEMPLATE

the front page child template filename

Type:

str

AUTH_URL

the url for the “Authenticate” front page link

Type:

str

EMBED_POST

the HTML for embedding a post. Should have a %(url)s placeholder for the post URL and optionally a %(content)s placeholder for the post content.

Type:

str

POST_ID_RE

regexp, optional, matches valid post ids. Used in post_id().

Type:

str

HTML2TEXT_OPTIONS

maps str html2text option names to values. https://github.com/Alir3z4/html2text/blob/master/docs/usage.md#available-options

Type:

dict

TRUNCATE_TEXT_LENGTH

optional character limit to truncate to. Defaults to Twitter’s limit, 280 characters as of 2019-10-12.

Type:

int

TRUNCATE_URL_LENGTH

optional number of characters that URLs count for. Defaults to Twitter’s, 23 as of 2019-10-12.

Type:

int

OPTIMIZED_COMMENTS

whether get_comment() is optimized and only fetches the requested comment. If False, get_comment() fetches many or all of the post’s comments to find the requested one.

Type:

bool

user_url(user_id)[source]

Returns the URL for a user’s profile.

get_actor(user_id=None)[source]

Fetches and returns a user.

Parameters:

user_id (str) – defaults to current user

Returns:

ActivityStreams actor

Return type:

dict

get_activities(*args, **kwargs)[source]

Fetches and returns a list of activities.

See get_activities_response() for args and kwargs.

Returns:

ActivityStreams activities

Return type:

list of dict

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, start_index=0, count=0, etag=None, min_id=None, cache=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, include_shares=True, fetch_events=False, fetch_mentions=False, search_query=None, scrape=False, **kwargs)[source]

Fetches and returns ActivityStreams activities and response details.

Subclasses should override this. See get_activities() for an alternative that just returns the list of activities.

If user_id is provided, only that user’s activity(s) are included. start_index and count determine paging, as described in the spec: http://activitystrea.ms/draft-spec.html#anchor14

app id is just object id: http://opensocial-resources.googlecode.com/svn/spec/2.0/Social-Data.xml#appId

group id is string id of group or @self, @friends, @all, @search: http://opensocial-resources.googlecode.com/svn/spec/2.0/Social-Data.xml#Group-ID

The fetch_* kwargs all default to False because they often require extra API round trips. Some sources return replies, likes, and shares in the same initial call, so they may be included even if you don’t set their kwarg to True.

Parameters:
  • user_id (str) – defaults to the currently authenticated user

  • group_id (str) – one of @self, @all, @friends, @search. defaults to @friends

  • app_id (str)

  • activity_id (str)

  • start_index (int) – >= 0

  • count (int) – >= 0

  • etag (str) – optional ETag to send with the API request. Results will only be returned if the ETag has changed. Should include enclosing double quotes, e.g. "ABC123"

  • min_id (only) – return activities with ids greater than this

  • cache (dict) – optional, used to cache metadata like comment and like counts per activity across calls. Used to skip expensive API calls that haven’t changed.

  • fetch_replies (bool) – whether to fetch each activity’s replies also

  • fetch_likes (bool) – whether to fetch each activity’s likes also

  • include_shares (bool) – whether to include share activities

  • fetch_shares (bool) – whether to fetch each activity’s shares also

  • fetch_events (bool) – whether to fetch the user’s events also

  • fetch_mentions (bool) – whether to fetch posts that mention the user

  • search_query (str) – an optional search query, only for use with @search group_id

  • scrape (bool) – whether to scrape activities from HTML (etc) instead of using an API. Not supported by all sources.

  • kwargs – some subclasses accept extra kwargs. See their docs for details.

Returns:

Response values based on OpenSocial ActivityStreams REST API.

The returned dict has at least these keys:

  • items (list of dict): activities

  • startIndex (int or None)

  • itemsPerPage (int)

  • totalResults (int or None, eg if it can’t be calculated efficiently)

  • filtered: False

  • sorted: False

  • updatedSince: False

  • etag (str): ETag returned by the API’s initial call to get activities

Return type:

dict

Raises:
  • ValueError – if any argument is invalid for this source

  • NotImplementedError – if the source doesn’t support the requested operation, eg Facebook doesn’t support search.

classmethod make_activities_base_response(activities, *args, **kwargs)[source]

Generates a base response dict for get_activities_response().

See get_activities() for args and kwargs.

scraped_to_activities(scraped, count=None, fetch_extras=False, cookie=None)[source]

Converts scraped HTML (or JSON, etc) to AS activities.

Used for scraping data from the web instead of using an API. Useful for sources with APIs that are restricted or have difficult approval processes.

Parameters:
  • scraped (str) – scraped data from a feed of posts

  • count (int) – number of activities to return, None for all

  • fetch_extras – whether to make extra HTTP fetches to get likes, etc.

  • cookie (str) – optional cookie to be used for subsequent HTTP fetches, if necessary.

Returns:

([AS activities], AS logged in actor (ie viewer))

Return type:

(list of dict, dict) tuple

scraped_to_activity(scraped)[source]

Converts scraped HTML (or JSON, etc) to a single AS activity.

Used for scraping data from the web instead of using an API. Useful for sources with APIs that are restricted or have difficult approval processes.

Parameters:

scraped (str) – scraped data from a single post permalink

Returns:

: (AS activity or None, AS logged in actor (ie viewer))

Return type:

(dict, dict) tuple

scraped_to_actor(scraped)[source]

Converts HTML from a profile page to an AS1 actor.

Args

html (str): HTML from a profile page

Returns:

AS1 actor

Return type:

dict

merge_scraped_reactions(scraped, activity)[source]

Converts and merges scraped likes and reactions into an activity.

New likes and emoji reactions are added to the activity in tags. Existing likes and emoji reactions in tags are ignored.

Parameters:
  • scraped (str) – HTML or JSON with likes and/or emoji reactions

  • activity (dict) – AS activity to merge these reactions into

Returns:

AS like/react tag objects converted from scraped

Return type:

list of dict

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a new object: a post, comment, like, share, or RSVP.

Subclasses should override this. Different sites will support different functionality; check each subclass for details. The actor will usually be the authenticated user.

Parameters:
  • obj (dict) – ActivityStreams object. At minimum, must have the content field. objectType is strongly recommended.

  • include_link (str) – INCLUDE_LINK, OMIT_LINK, or INCLUDE_IF_TRUNCATED; whether to include a link to the object (if it has one) in the content.

  • ignore_formatting (bool) – whether to use content text as is, instead of converting its HTML to plain text styling (newlines, etc.)

Returns:

The result. content will be a dict or None. If the newly created object has an id or permalink, they’ll be provided in the values for id and url.

Return type:

CreationResult

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Previews creating a new object: a post, comment, like, share, or RSVP.

Returns HTML that previews what create() with the same object will do.

Subclasses should override this. Different sites will support different functionality; check each subclass for details. The actor will usually be the authenticated user.

Parameters:
  • obj (dict) – ActivityStreams object. At minimum, must have the content field. objectType is strongly recommended.

  • include_link (str) – INCLUDE_LINK, OMIT_LINK, or INCLUDE_IF_TRUNCATED; whether to include a link to the object (if it has one) in the content.

  • ignore_formatting (bool) – whether to use content text as is, instead of converting its HTML to plain text styling (newlines, etc.)

Returns:

The result. content will be a dict or None.

Return type:

CreationResult

delete(id)[source]

Deletes a post.

Generally only supports posts that were authored by the authenticating user.

Parameters:

id (str) – silo object id

Return type:

CreationResult

preview_delete(id)[source]

Previews deleting a post.

Parameters:

id (str) – silo object id

Return type:

CreationResult

get_event(event_id)[source]

Fetches and returns an event.

Parameters:

id (str) – site-specific event id

Returns:

decoded ActivityStreams activity, or None

Return type:

dict

get_comment(comment_id, activity_id=None, activity_author_id=None, activity=None)[source]

Fetches and returns a comment.

Subclasses should override this.

Parameters:
  • comment_id (str) – comment id

  • activity_id (str) – activity id, optional

  • activity_author_id (str) – activity author id, optional. Needed for some sources (e.g. Facebook) to construct the comment permalink.

  • activity (dict) – activity object, optional. May avoid an API call if provided.

Returns:

ActivityStreams comment object

Return type:

dict

Raises:

ValueError – if any argument is invalid for this source

get_like(activity_user_id, activity_id, like_user_id, activity=None)[source]

Fetches and returns a ‘like’.

Default implementation that fetches the activity and its likes, then searches for a like by the given user. Subclasses should override this if they can optimize the process.

Parameters:
  • activity_user_id (str) – id of the user who posted the original activity

  • activity_id (str) – activity id

  • like_user_id (str) – id of the user who liked the activity

  • activity (dict) – activity object, optional. May avoid an API call if provided.

Returns:

ActivityStreams like activity

Return type:

dict

get_reaction(activity_user_id, activity_id, reaction_user_id, reaction_id, activity=None)[source]

Fetches and returns a reaction.

Default implementation that fetches the activity and its reactions, then searches for this specific reaction. Subclasses should override this if they can optimize the process.

Parameters:
  • activity_user_id (str) – id of the user who posted the original activity

  • activity_id (str) – activity id

  • reaction_user_id (str) – id of the user who reacted

  • reaction_id (str) – id of the reaction

  • activity (dict) – activity object, optional. May avoid an API call if provided.

Returns:

ActivityStreams reaction activity

Return type:

dict

get_share(activity_user_id, activity_id, share_id, activity=None)[source]

Fetches and returns a share.

Parameters:
  • activity_user_id (str) – id of the user who posted the original activity

  • activity_id (str) – activity id

  • share_id (str) – id of the share object or the user who shared it

  • activity (dict) – activity object, optional. May avoid an API call if provided.

Returns:

an ActivityStreams share activity

Return type:

dict

get_rsvp(activity_user_id, event_id, user_id, event=None)[source]

Fetches and returns an RSVP.

Parameters:
  • activity_user_id (str) – id of the user who posted the event. unused.

  • event_id (str) – event id

  • user_id (str) – user id

  • event (dict) – AS event activity, optional

Returns: dict, an ActivityStreams RSVP activity object

get_blocklist()[source]

Fetches and returns the current user’s block list.

…ie the users that the current user is blocking. The exact semantics of blocking vary from silo to silo.

Returns:

actor objects

Return type:

sequence of dict

get_blocklist_ids()[source]

Returns the current user’s block list as a list of silo-specific user ids.

Returns:

user ids, not globally unique across other sources

Return type:

sequence of int or str

user_to_actor(user)[source]

Converts a user to an actor.

The returned object will have at least a url field. If the user has multiple URLs, there will also be a urls list field whose elements are dicts with value URL.

Parameters:

user (dict) – a decoded JSON silo user object

Returns:

ActivityStreams actor

Return type:

dict

static postprocess_activity(activity, mentions=False)[source]

Does source-independent post-processing of an activity, in place.

Right now just populates the title field.

Parameters:
  • activity (dict)

  • mentions (boolean) – whether to detect @-mention links and convert them to mention tags

static postprocess_object(obj, mentions=False)[source]

Does source-independent post-processing of an object, in place.

  • Populates location.position based on latitude and longitude.

  • Optionally interprets HTML links in content with text starting with @, eg @user or @user.com or @user@instance.com, as @-mentions and adds mention tags for them.

Parameters:
  • obj (dict)

  • mentions (boolean) – whether to detect @-mention links and convert them to mention tags

Returns:

obj, modified in place

Return type:

dict

classmethod embed_post(obj)[source]

Returns the HTML string for embedding a post object.

Parameters:

obj (dict) – AS1 object with at least url, optionally also content.

Returns:

HTML

Return type:

str

classmethod embed_actor(actor)[source]

Returns the HTML string for embedding an actor object.

tag_uri(name)[source]

Returns a tag URI string for this source and the given string name.

base_object(obj)[source]

Returns the base silo object that an object operates on.

For example, if the object is a comment, this returns the post that it’s a comment on. If it’s an RSVP, this returns the event. The id in the returned object is silo-specific, ie not a tag URI.

Subclasses may override this.

Parameters:

obj (dict) – ActivityStreams object

Returns:

minimal ActivityStreams object. Usually has at least id; may also have url, author, etc.

Return type:

dict

classmethod base_id(url)[source]

Guesses the id of the object in the given URL.

Returns:

str or None

classmethod post_id(url)[source]

Guesses the post id of the given URL.

Returns:

str or None

truncate(content, url, include_link, type=None, quote_url=None)[source]

Shorten text content to fit within a character limit.

Character limit and URL character length are taken from TRUNCATE_TEXT_LENGTH and TRUNCATE_URL_LENGTH.

Parameters:
  • content (str)

  • url (str)

  • include_link (str) – OMIT_LINK, INCLUDE_LINK, or INCLUDE_IF_TRUNCATED

  • type (str) – optional: article, note, etc.

  • quote_url (str) – URL, optional. If provided, it will be appended to the content, after truncating.

Returns:

the possibly shortened and ellipsized text

Return type:

str

twitter

Twitter source class.

Uses the v1.1 REST API: https://developer.twitter.com/en/docs/api-reference-index

The Audience Targeting to field is set to @public or @private based on whether the tweet author’s protected field is true or false. https://dev.twitter.com/docs/platform-objects/users

class granary.twitter.OffsetTzinfo(utc_offset=0)[source]

Bases: tzinfo

A simple, DST-unaware tzinfo from given utc offset in seconds.

__init__(utc_offset=0)[source]

Constructor.

Parameters:

utc_offset – Offset of time zone from UTC in seconds

utcoffset(dt)[source]

datetime -> timedelta showing offset from UTC, negative values indicating West of UTC

dst(dt)[source]

datetime -> DST offset as timedelta positive east of UTC.

class granary.twitter.Twitter(access_token_key, access_token_secret, username=None, scrape_headers=None)[source]

Bases: Source

Twitter source class. See file docstring and Source for details.

__init__(access_token_key, access_token_secret, username=None, scrape_headers=None)[source]

Constructor.

Twitter now requires authentication in v1.1 of their API. You can get an OAuth access token by creating an app here: https://dev.twitter.com/apps/new

Parameters:
  • access_token_key (str) – OAuth access token key

  • access_token_secret (str) – OAuth access token secret

  • username (str) – optional, the current user. Used in e.g. preview/create.

  • scrape_headers (dict) – optional, with string HTTP header keys and values to use when scraping likes

get_actor(screen_name=None)[source]

Returns a user as a JSON ActivityStreams actor dict.

Parameters:

screen_name (str) – username. Defaults to the current user.

get_activities_response(user_id=None, group_id=None, app_id=None, activity_id=None, start_index=0, count=0, etag=None, min_id=None, cache=None, fetch_replies=False, fetch_likes=False, fetch_shares=False, include_shares=True, fetch_events=False, fetch_mentions=False, search_query=None, scrape=False, **kwargs)[source]

Fetches posts and converts them to ActivityStreams activities.

See source.Source.get_activities_response() for details. app_id is ignored. min_id is translated to Twitter’s since_id.

The code for handling ETags (and 304 Not Changed responses and setting If-None-Match) is here, but unused right now since Twitter evidently doesn’t support ETags. From https://dev.twitter.com/discussions/5800 : “I’ve confirmed with our team that we’re not explicitly supporting this family of features.”

Likes (nee favorites) are scraped from twitter.com, since Twitter’s REST API doesn’t offer a way to fetch them. You can also get them from the Streaming API, though, and convert them with streaming_event_to_object(). https://dev.twitter.com/docs/streaming-apis/messages#Events_event

Shares (ie retweets) are fetched with a separate API call per tweet: https://dev.twitter.com/docs/api/1.1/get/statuses/retweets/%3Aid

However, retweets are only fetched for the first 15 tweets that have them, since that’s Twitter’s rate limit per 15 minute window. :( https://dev.twitter.com/docs/rate-limiting/1.1/limits

Quote tweets are fetched by searching for the possibly quoted tweet’s ID, using the OR operator to search up to 5 IDs at a time, and then checking the quoted_status_id_str field: https://dev.twitter.com/overview/api/tweets#quoted_status_id_str

Use the group_id @self to retrieve a user_id’s timeline. If user_id is None or @me, it will return tweets for the current API user.

group_id can be used to specify the slug of a list for which to return tweets. By default the current API user’s lists will be used, but lists owned by other users can be fetched by explicitly passing a username to user_id, e.g. to fetch tweets from the list @exampleuser/example-list you would call get_activities(user_id='exampleuser', group_id='example-list').

Twitter replies default to including a mention of the user they’re replying to, which overloads mentions a bit. When fetch_mentions is True, we determine that a tweet mentions the current user if it @-mentions their username and:

  • it’s not a reply, OR

  • it’s a reply, but not to the current user, AND

  • the tweet it’s replying to doesn’t @-mention the current user

Raises:

NotImplementedError – if fetch_likes is True but scrape_headers was not provided to the constructor.

XXX HACK: this is currently hacked for Bridgy to NOT pass min_id to the request for fetching activity tweets themselves, but to pass it to all of the requests for filling in replies, retweets, etc. That’s because we want to find new replies and retweets of older initial tweets. TODO: find a better way.

fetch_replies(activities, min_id=None)[source]

Fetches and injects Twitter replies into a list of activities, in place.

Includes indirect replies ie reply chains, not just direct replies. Searches for @-mentions, matches them to the original tweets with in_reply_to_status_id_str, and recurses until it’s walked the entire tree.

Parameters:

activities (list of dict)

Returns:

same activities

Return type:

list of dict

fetch_mentions(username, tweets, min_id=None)[source]

Fetches a user’s @-mentions and returns them as ActivityStreams.

Tries to only include explicit mentions, not mentions automatically created by @-replying. See get_activities_response() for details.

Parameters:
  • username (str)

  • tweets (list) – of Twitter API objects. used to find quote tweets quoting them.

  • min_id (str) – only return activities with ids greater than this

Returns:

activities

Return type:

list of dict

get_comment(comment_id, activity_id=None, activity_author_id=None, activity=None)[source]

Returns an ActivityStreams comment object.

Parameters:
  • comment_id (str) – comment id

  • activity_id (str) – activity id, optional

  • activity_author_id (str) – activity author id; ignored

  • activity (dict) – original object, optional

get_share(activity_user_id, activity_id, share_id, activity=None)[source]

Returns an ActivityStreams ‘share’ activity object.

Parameters:
  • activity_user_id (str) – id of the user who posted the original activity

  • activity_id (str) – activity id

  • share_id (str) – id of the share object

  • activity (dict) – original object, optional

get_blocklist()[source]

Returns the current user’s block list.

May make multiple API calls, using cursors, to fully fetch large blocklists. https://dev.twitter.com/overview/api/cursoring

Block lists may have up to 10k users, but each API call only returns 100 at most, and the API endpoint is rate limited to 15 calls per user per 15m. So if a user has >1500 users on their block list, we can’t get the whole thing at once. :(

Returns:

actors

Return type:

list of dict

Raises:
  • source.RateLimited – if we hit the rate limit. The partial attribute will

  • have the list of user ids we fetched before hitting the limit.

get_blocklist_ids()[source]

Returns the current user’s block list as a list of Twitter user ids.

May make multiple API calls, using cursors, to fully fetch large blocklists. https://dev.twitter.com/overview/api/cursoring

Subject to the same rate limiting as get_blocklist(), but each API call returns ~4k ids, so realistically this can actually fetch blocklists of up to 75k users at once. Beware though, many Twitter users have even more!

Returns:

Twitter user ids

Return type:

sequence of str

Raises:
  • source.RateLimited – if we hit the rate limit. The partial attribute will

  • have the list of user ids we fetched before hitting the limit.

create(obj, include_link='omit', ignore_formatting=False)[source]

Creates a tweet, reply tweet, retweet, or favorite.

Parameters:
  • obj (dict) – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

content will be a dict with id, url, and type keys (all optional) for the newly created Twitter object (or None)

Return type:

CreationResult

preview_create(obj, include_link='omit', ignore_formatting=False)[source]

Previews creating a tweet, reply tweet, retweet, or favorite.

Parameters:
  • obj – ActivityStreams object

  • include_link (str)

  • ignore_formatting (bool)

Returns:

content will be an HTML snippet

Return type:

CreationResult or None

upload_images(images)[source]

Uploads one or more images from web URLs.

https://dev.twitter.com/rest/reference/post/media/upload

Note that files and JSON bodies in media POST API requests are not included in OAuth signatures. https://developer.twitter.com/en/docs/media/upload-media/uploading-media/media-best-practices

Parameters:

images (sequence of dict) –

AS image objects, eg:

[{'url': 'http://picture', 'displayName': 'a thing'}, ...]

Returns:

media ids

Return type:

list of str, or CreationResult on error

upload_video(url)[source]

Uploads a video from a web URL using the chunked upload process.

Chunked upload consists of multiple API calls:

  • command=INIT, which allocates the media id

  • command=APPEND for each 5MB block, up to 15MB total

  • command=FINALIZE

  • command=STATUS to wait until Twitter finishes processing the video

https://developer.twitter.com/en/docs/media/upload-media/uploading-media/chunked-media-upload

Parameters:

url (str) – URL of video

Returns:

media id

Return type:

str, or CreationResult on error

delete(id)[source]

Deletes a tweet. The authenticated user must have authored it.

Parameters:

id (int or str) – tweet id to delete

Returns:

content is Twitter API response dict

Return type:

CreationResult

preview_delete(id)[source]

Previews deleting a tweet.

Parameters:

id (int or str) – tweet id to delete

Return type:

CreationResult

urlopen(url, parse_response=True, **kwargs)[source]

Wraps urllib.request.urlopen() and adds an OAuth signature.

base_object(obj)[source]

Returns the “base” silo object that an object operates on.

Includes special handling for Twitter photo and video URLs, eg:

  • https://twitter.com/nelson/status/447465082327298048/photo/1

  • https://twitter.com/nelson/status/447465082327298048/video/1

Parameters:

obj (dict) – ActivityStreams object

Returns:

minimal ActivityStreams object. Usually has at least id and url fields; may also have author.

Return type:

dict

tweet_to_activity(tweet)[source]

Converts a tweet to an activity.

Parameters:

tweet (dict) – a decoded JSON tweet

Returns:

ActivityStreams activity

Return type:

dict

tweet_to_object(tweet)[source]

Converts a tweet to an object.

Parameters:

tweet (dict) – a decoded JSON tweet

Returns:

an ActivityStreams object dict, ready to be JSON-encoded

user_to_actor(user)[source]

Converts a user to an actor.

Parameters:

user (dict) – a decoded JSON Twitter user

Returns:

ActivityStreams actor

Return type:

dict

retweet_to_object(retweet)[source]

Converts a retweet to a share activity object.

Parameters:

retweet (dict) – a decoded JSON tweet

Returns:

ActivityStreams object

Return type:

dict

streaming_event_to_object(event)[source]

Converts a Streaming API event to an object.

https://dev.twitter.com/docs/streaming-apis/messages#Events_event

Right now, only converts favorite events to like objects.

Parameters:

event (dict) – a decoded JSON Streaming API event

Returns:

ActivityStreams object

Return type:

dict

static rfc2822_to_iso8601(time_str)[source]

Converts a timestamp string from RFC 2822 format to ISO 8601.

Example RFC 2822 timestamp string generated by Twitter:

Wed May 23 06:01:13 +0000 2007

Resulting ISO 8610 timestamp string:

2007-05-23T06:01:13

user_url(username)[source]

Returns the Twitter URL for a given user.

status_url(username, id)[source]

Returns the Twitter URL for a tweet from a given user with a given id.

tweet_url(tweet)[source]

Returns the Twitter URL for a tweet given a tweet object.