|
|
@@ -4,8 +4,11 @@ import { type Cache, MemoryCache, NoopCache } from './cache';
|
|
|
|
|
|
|
|
|
|
|
|
/** Sentiment label assigned to a mention by Socialhose. */
|
|
|
|
/** Sentiment label assigned to a mention by Socialhose. */
|
|
|
|
export type Sentiment = 'positive' | 'negative' | 'neutral';
|
|
|
|
export type Sentiment = 'positive' | 'negative' | 'neutral';
|
|
|
|
|
|
|
|
/** Count distribution across positive, negative, and neutral sentiment labels. */
|
|
|
|
export type SentimentSplit = { positive: number; negative: number; neutral: number };
|
|
|
|
export type SentimentSplit = { positive: number; negative: number; neutral: number };
|
|
|
|
|
|
|
|
/** Primitive value accepted as a query-string parameter. Nullish and empty-string values are omitted. */
|
|
|
|
export type QueryValue = string | number | boolean | null | undefined;
|
|
|
|
export type QueryValue = string | number | boolean | null | undefined;
|
|
|
|
|
|
|
|
/** Query-string parameter map used by low-level GET calls. */
|
|
|
|
export type QueryParams = Record<string, QueryValue>;
|
|
|
|
export type QueryParams = Record<string, QueryValue>;
|
|
|
|
|
|
|
|
|
|
|
|
/** Configuration for {@link SocialhoseClient}. */
|
|
|
|
/** Configuration for {@link SocialhoseClient}. */
|
|
|
@@ -34,6 +37,7 @@ export interface RequestOptions {
|
|
|
|
headers?: Record<string, string>;
|
|
|
|
headers?: Record<string, string>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Campaign metadata returned by `/campaigns/`. */
|
|
|
|
export interface Campaign {
|
|
|
|
export interface Campaign {
|
|
|
|
id: string;
|
|
|
|
id: string;
|
|
|
|
name: string;
|
|
|
|
name: string;
|
|
|
@@ -44,6 +48,7 @@ export interface Campaign {
|
|
|
|
tags: string[];
|
|
|
|
tags: string[];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Aggregate metrics returned by `/analytics/overview/`. */
|
|
|
|
export interface Overview {
|
|
|
|
export interface Overview {
|
|
|
|
total_mentions: number;
|
|
|
|
total_mentions: number;
|
|
|
|
total_authors: number;
|
|
|
|
total_authors: number;
|
|
|
@@ -66,6 +71,7 @@ export interface Overview {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** One time bucket returned by `/analytics/timeline/`. */
|
|
|
|
export interface TimelinePoint {
|
|
|
|
export interface TimelinePoint {
|
|
|
|
date: string;
|
|
|
|
date: string;
|
|
|
|
count: number;
|
|
|
|
count: number;
|
|
|
@@ -73,6 +79,7 @@ export interface TimelinePoint {
|
|
|
|
engagement: number;
|
|
|
|
engagement: number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Campaign comparison row returned by `/analytics/share-of-voice/`. */
|
|
|
|
export interface ShareOfVoiceItem {
|
|
|
|
export interface ShareOfVoiceItem {
|
|
|
|
campaign_id: string;
|
|
|
|
campaign_id: string;
|
|
|
|
name: string;
|
|
|
|
name: string;
|
|
|
@@ -82,18 +89,21 @@ export interface ShareOfVoiceItem {
|
|
|
|
sentiment: SentimentSplit;
|
|
|
|
sentiment: SentimentSplit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Mention and engagement totals for one platform. */
|
|
|
|
export interface PlatformStat {
|
|
|
|
export interface PlatformStat {
|
|
|
|
platform: string;
|
|
|
|
platform: string;
|
|
|
|
count: number;
|
|
|
|
count: number;
|
|
|
|
engagement: number;
|
|
|
|
engagement: number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Top-keyword row with mention count and sentiment split. */
|
|
|
|
export interface KeywordStat {
|
|
|
|
export interface KeywordStat {
|
|
|
|
keyword: string;
|
|
|
|
keyword: string;
|
|
|
|
count: number;
|
|
|
|
count: number;
|
|
|
|
sentiment: SentimentSplit;
|
|
|
|
sentiment: SentimentSplit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Trending-keyword row comparing current and previous mention volume. */
|
|
|
|
export interface TrendingItem {
|
|
|
|
export interface TrendingItem {
|
|
|
|
keyword: string;
|
|
|
|
keyword: string;
|
|
|
|
count: number;
|
|
|
|
count: number;
|
|
|
@@ -101,6 +111,7 @@ export interface TrendingItem {
|
|
|
|
change_pct: number;
|
|
|
|
change_pct: number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Compact high-impact mention returned by the analytics top-mentions endpoint. */
|
|
|
|
export interface TopMention {
|
|
|
|
export interface TopMention {
|
|
|
|
id: string;
|
|
|
|
id: string;
|
|
|
|
platform: string;
|
|
|
|
platform: string;
|
|
|
@@ -115,6 +126,7 @@ export interface TopMention {
|
|
|
|
content_preview: string;
|
|
|
|
content_preview: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Full mention record returned by `/mentions/`, including content, engagement, metadata, and author profile. */
|
|
|
|
export interface Mention {
|
|
|
|
export interface Mention {
|
|
|
|
id: string;
|
|
|
|
id: string;
|
|
|
|
platform: string;
|
|
|
|
platform: string;
|
|
|
@@ -147,6 +159,7 @@ export interface Mention {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Mailing-list metadata returned by `/mailing-lists/`. */
|
|
|
|
export interface MailingList {
|
|
|
|
export interface MailingList {
|
|
|
|
id: string;
|
|
|
|
id: string;
|
|
|
|
campaign_id: string;
|
|
|
|
campaign_id: string;
|
|
|
@@ -157,6 +170,7 @@ export interface MailingList {
|
|
|
|
email_template: string;
|
|
|
|
email_template: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Invitation state returned when adding a mailing-list member. */
|
|
|
|
export interface MailingListInvitation {
|
|
|
|
export interface MailingListInvitation {
|
|
|
|
id: string;
|
|
|
|
id: string;
|
|
|
|
email: string;
|
|
|
|
email: string;
|
|
|
@@ -169,14 +183,17 @@ export interface MailingListInvitation {
|
|
|
|
accepted_at: string | null;
|
|
|
|
accepted_at: string | null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Normalized mailing-list invite outcome. */
|
|
|
|
export type InviteOutcome = 'invited' | 'already' | 'error';
|
|
|
|
export type InviteOutcome = 'invited' | 'already' | 'error';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Normalized response from {@link SocialhoseClient.inviteMailingListMember}. */
|
|
|
|
export interface InviteResult {
|
|
|
|
export interface InviteResult {
|
|
|
|
outcome: InviteOutcome;
|
|
|
|
outcome: InviteOutcome;
|
|
|
|
invitation?: MailingListInvitation;
|
|
|
|
invitation?: MailingListInvitation;
|
|
|
|
detail?: string;
|
|
|
|
detail?: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Standard paginated response envelope used by list endpoints. */
|
|
|
|
export interface Paginated<T> {
|
|
|
|
export interface Paginated<T> {
|
|
|
|
count: number;
|
|
|
|
count: number;
|
|
|
|
next: string | null;
|
|
|
|
next: string | null;
|
|
|
@@ -184,6 +201,7 @@ export interface Paginated<T> {
|
|
|
|
results: T[];
|
|
|
|
results: T[];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Common filters accepted by analytics endpoints. Values are passed through as query parameters. */
|
|
|
|
export interface AnalyticsFilters {
|
|
|
|
export interface AnalyticsFilters {
|
|
|
|
campaign_ids?: string;
|
|
|
|
campaign_ids?: string;
|
|
|
|
date_from?: string;
|
|
|
|
date_from?: string;
|
|
|
@@ -192,6 +210,7 @@ export interface AnalyticsFilters {
|
|
|
|
sentiments?: string;
|
|
|
|
sentiments?: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Filters accepted by `/mentions/`, extending common analytics filters with pagination, search, and ordering. */
|
|
|
|
export interface MentionFilters extends AnalyticsFilters {
|
|
|
|
export interface MentionFilters extends AnalyticsFilters {
|
|
|
|
page?: number;
|
|
|
|
page?: number;
|
|
|
|
content_search?: string;
|
|
|
|
content_search?: string;
|
|
|
@@ -207,11 +226,13 @@ export interface MentionFilters extends AnalyticsFilters {
|
|
|
|
// when count <= 20, which covers most entities at current volume); a few
|
|
|
|
// when count <= 20, which covers most entities at current volume); a few
|
|
|
|
// count-only requests add precise week-over-week momentum.
|
|
|
|
// count-only requests add precise week-over-week momentum.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Platform count pair used by entity analytics. */
|
|
|
|
export interface PlatformShare {
|
|
|
|
export interface PlatformShare {
|
|
|
|
platform: string;
|
|
|
|
platform: string;
|
|
|
|
count: number;
|
|
|
|
count: number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Compact analytics for one term from a single mention search. */
|
|
|
|
export interface EntityBrief {
|
|
|
|
export interface EntityBrief {
|
|
|
|
term: string;
|
|
|
|
term: string;
|
|
|
|
total: number;
|
|
|
|
total: number;
|
|
|
@@ -221,6 +242,7 @@ export interface EntityBrief {
|
|
|
|
sample: Mention[]; // up to 20, ordered by engagement
|
|
|
|
sample: Mention[]; // up to 20, ordered by engagement
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Rich entity dashboard assembled from multiple mention-search facets. */
|
|
|
|
export interface EntityStats extends EntityBrief {
|
|
|
|
export interface EntityStats extends EntityBrief {
|
|
|
|
recent: Mention[]; // up to 20, newest first
|
|
|
|
recent: Mention[]; // up to 20, newest first
|
|
|
|
recent7d: number;
|
|
|
|
recent7d: number;
|
|
|
@@ -391,6 +413,7 @@ export class SocialhoseClient {
|
|
|
|
return { ...d, total_mentions: num(d.total_mentions), total_authors: num(d.total_authors) };
|
|
|
|
return { ...d, total_mentions: num(d.total_mentions), total_authors: num(d.total_authors) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch analytics time-series buckets; defaults to daily interval. */
|
|
|
|
async getTimeline(
|
|
|
|
async getTimeline(
|
|
|
|
filters: AnalyticsFilters & { interval?: 'day' | 'week' | 'month' } = {},
|
|
|
|
filters: AnalyticsFilters & { interval?: 'day' | 'week' | 'month' } = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
@@ -403,6 +426,7 @@ export class SocialhoseClient {
|
|
|
|
return d.series ?? [];
|
|
|
|
return d.series ?? [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch overall and platform-specific sentiment distributions. */
|
|
|
|
getSentiment(
|
|
|
|
getSentiment(
|
|
|
|
filters: AnalyticsFilters = {},
|
|
|
|
filters: AnalyticsFilters = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
@@ -410,6 +434,7 @@ export class SocialhoseClient {
|
|
|
|
return this.get('/analytics/sentiment/', filters as QueryParams, options);
|
|
|
|
return this.get('/analytics/sentiment/', filters as QueryParams, options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch campaign share-of-voice comparison metrics. */
|
|
|
|
getShareOfVoice(
|
|
|
|
getShareOfVoice(
|
|
|
|
filters: AnalyticsFilters = {},
|
|
|
|
filters: AnalyticsFilters = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
@@ -417,11 +442,13 @@ export class SocialhoseClient {
|
|
|
|
return this.get('/analytics/share-of-voice/', filters as QueryParams, options);
|
|
|
|
return this.get('/analytics/share-of-voice/', filters as QueryParams, options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch mention and engagement totals grouped by platform. */
|
|
|
|
async getPlatforms(filters: AnalyticsFilters = {}, options: RequestOptions = {}): Promise<PlatformStat[]> {
|
|
|
|
async getPlatforms(filters: AnalyticsFilters = {}, options: RequestOptions = {}): Promise<PlatformStat[]> {
|
|
|
|
const d = await this.get<{ platforms?: PlatformStat[] }>('/analytics/platforms/', filters as QueryParams, options);
|
|
|
|
const d = await this.get<{ platforms?: PlatformStat[] }>('/analytics/platforms/', filters as QueryParams, options);
|
|
|
|
return d.platforms ?? [];
|
|
|
|
return d.platforms ?? [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch top keywords; defaults to 12 items. */
|
|
|
|
async getTopKeywords(
|
|
|
|
async getTopKeywords(
|
|
|
|
filters: AnalyticsFilters & { limit?: number } = {},
|
|
|
|
filters: AnalyticsFilters & { limit?: number } = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
@@ -434,6 +461,7 @@ export class SocialhoseClient {
|
|
|
|
return d.keywords ?? [];
|
|
|
|
return d.keywords ?? [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch trending keywords; defaults to 8 items. */
|
|
|
|
async getTrending(
|
|
|
|
async getTrending(
|
|
|
|
filters: AnalyticsFilters & { limit?: number } = {},
|
|
|
|
filters: AnalyticsFilters & { limit?: number } = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
@@ -446,6 +474,7 @@ export class SocialhoseClient {
|
|
|
|
return d.trending ?? [];
|
|
|
|
return d.trending ?? [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch top mentions by impact; defaults to 6 items. */
|
|
|
|
async getTopMentions(
|
|
|
|
async getTopMentions(
|
|
|
|
filters: AnalyticsFilters & { limit?: number } = {},
|
|
|
|
filters: AnalyticsFilters & { limit?: number } = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
|
options: RequestOptions = {},
|
|
|
@@ -458,6 +487,7 @@ export class SocialhoseClient {
|
|
|
|
return d.mentions ?? [];
|
|
|
|
return d.mentions ?? [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch a paginated mention page with campaign/date/platform/sentiment/search/order filters. */
|
|
|
|
getMentions(
|
|
|
|
getMentions(
|
|
|
|
filters: MentionFilters = {},
|
|
|
|
filters: MentionFilters = {},
|
|
|
|
optionsOrRevalidate: RequestOptions | number = {},
|
|
|
|
optionsOrRevalidate: RequestOptions | number = {},
|
|
|
@@ -466,11 +496,13 @@ export class SocialhoseClient {
|
|
|
|
return this.get<Paginated<Mention>>('/mentions/', { page: 1, ...filters }, options);
|
|
|
|
return this.get<Paginated<Mention>>('/mentions/', { page: 1, ...filters }, options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fetch the first page of mailing lists. */
|
|
|
|
async getMailingLists(options: RequestOptions = {}): Promise<MailingList[]> {
|
|
|
|
async getMailingLists(options: RequestOptions = {}): Promise<MailingList[]> {
|
|
|
|
const d = await this.get<Paginated<MailingList>>('/mailing-lists/', { page: 1 }, options);
|
|
|
|
const d = await this.get<Paginated<MailingList>>('/mailing-lists/', { page: 1 }, options);
|
|
|
|
return d.results;
|
|
|
|
return d.results;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Invite a recipient to a mailing list and normalize invited/already/error outcomes. */
|
|
|
|
async inviteMailingListMember(
|
|
|
|
async inviteMailingListMember(
|
|
|
|
listId: string,
|
|
|
|
listId: string,
|
|
|
|
invite: { email: string; first_name?: string; last_name?: string; invitation_message?: string },
|
|
|
|
invite: { email: string; first_name?: string; last_name?: string; invitation_message?: string },
|
|
|
|