Skip to content

angee.social.models

Source models for the social addon — public feeds, engagement, and following.

Social is the public-social surface layered on messaging. It reuses the one idempotent Message.objects.ingest write path (a public post is a messaging.Message in a messaging.Thread) and adds the social overlay:

  • :class:Feed — an integrate.Integration child + Bridge (exactly like messaging.Channel) that polls an external platform for public posts; its FeedBackend does the transport+parse, and sync() maps each post onto the messaging ingest, then overlays engagement.
  • :class:FeedFollow — the following / timeline subscription edge.
  • :class:PostMetrics — rolled-up public engagement counts for a message.
  • per-actor social reactions (like / repost / emoji) reuse the single messaging.Reaction table — social writes like/repost as reaction values on the shared messaging.Message rather than owning a parallel table.
  • :class:Quota — the per-handle, per-platform API-unit ledger feed backends spend.
  • :class:ThreadPublic / :class:MessagePublic — the public-thread fields social contributes onto messaging.Thread / messaging.Message through the same-row extends seam.

The dependency points one way (social → messaging → parties/integrate/storage); social never edits or forks messaging.

Feed

python
class Feed(Bridge)

A connected public-content source that polls an external platform for posts.

An integrate.Integration child (identity / credential / status / owner from the connection substrate) and a Bridge (the scheduler + run_sync drive it through sync; integrate.scheduler.run_due_bridges auto-discovers any concrete Bridge subclass, so no registration is needed). backend_class selects the platform — youtube / facebook are contributed by downstream social_integrate_* addons; manual is the neutral null-object.

A paused feed carries a NULL next_sync_at (not scheduled); activating it schedules the first poll. handle is the parties.Handle the feed monitors and posts as (its OAuth token lives on the handle / the integration credential).

backend_class

Registry key for the feed backend bound to this feed.

external_id

The external channel/page/account id this feed follows on its platform.

Meta

python
class Meta()

Django model options for the feed child model.

backend

python
@property
def backend() -> FeedBackend

Return this feed's selected backend, bound to this row.

sync

python
def sync() -> int

Fetch new posts, ingest their message core, and overlay engagement.

The message core (thread/message/parts) is the messaging owner's job, so a public post shares email's one idempotent write path; social only writes the overlay it owns (public payload / metrics / reactions / social edges). The ingest is told the facts a public feed differs on, each set through the messaging owner rather than bulk-patched afterward: every thread is born a PUBLIC_THREAD with PUBLIC visibility, each message lands under the COMMENT kind (a public post, not email), and the RFC-5322 quotation builder is skipped (quote_edges=False) so a post's short shared text does not mint spurious email quote edges.

FeedFollow

python
class FeedFollow(SqidMixin, AuditMixin, AngeeModel)

A follow of a :class:Feed by a parties.Handle — the timeline subscription.

The following edge behind a public timeline: a handle subscribes to a feed's posts. ended_at closes a follow (an open/closed interval), so unfollowing is an update, not a delete, and the history is retained. The timeline itself is the derived join FeedFollow → Feed → Thread → Message (a downstream query owner).

Meta

python
class Meta()

Django model options for the feed-follow source model.

__str__

python
def __str__() -> str

Return a readable follow label for Django displays.

PostMetrics

python
class PostMetrics(SqidMixin, AuditMixin, AngeeModel)

Rolled-up public engagement counts for one message (the platform snapshot).

Flat one-to-one, not MTI — the counter set overlaps heavily across platforms; platform extras go in metadata. Counters are overwritten with the latest platform snapshot (no F() delta), so the feed sync is the single writer.

Meta

python
class Meta()

Django model options for the post-metrics source model.

__str__

python
def __str__() -> str

Return a readable metrics label for Django displays.

Quota

python
class Quota(SqidMixin, AuditMixin, AngeeModel)

A per-handle, per-platform API-unit ledger for one billing period.

Feed backends spend platform API units (search, list, insert) against a per-period budget; :meth:~angee.social.managers.QuotaManager.consume atomically debits this ledger and refuses when the budget is insufficient. Enforcement is cooperative — the backend must ask before it spends.

Meta

python
class Meta()

Django model options for the quota source model.

__str__

python
def __str__() -> str

Return a readable quota label for Django displays.

ThreadPublic

python
class ThreadPublic(AngeeModel)

Public-post payload fields social contributes onto messaging.Thread (same row).

A public thread's row is its subject post: subject_url/body/tags carry the post payload and parent nests a thread under another (a reply/quote thread). These have no producer in base messaging, so social owns them; the structural modality/visibility discriminators stay owned by messaging.

Meta

python
class Meta()

Abstract same-row extension composed into messaging.Thread.

MessagePublic

python
class MessagePublic(AngeeModel)

Public-post fields social contributes onto messaging.Message (same row).

is_original_post marks the root post of a public thread (a post with no parent). It has no producer in base messaging, so social owns it and the composer folds it onto the single messaging.Message table.

Meta

python
class Meta()

Abstract same-row extension composed into messaging.Message.

Released under the AGPL-3.0 License.