mirror of
https://github.com/simstudioai/sim.git
synced 2026-10-12 23:33:47 +08:00
feat(similarweb): add page views tool, fix paid referrals field mismatch (#5354)
* feat(similarweb): add page views tool, fix paid referrals field mismatch - Add similarweb_page_views tool (Total Page Views, Desktop & Mobile) - Fix paidReferrals in website overview: API Lite response key is literally "paid _referrals" (space before underscore), not caught by the existing fallback chain, so it always resolved to null - Move startDate/endDate/mainDomainOnly to advanced mode * fix(similarweb): accept both page_views key spellings in page views response Cursor Bugbot and Greptile both flagged the response field as page_views by convention, but SimilarWeb's own docs example response uses pages_views for this specific endpoint. Accept both spellings defensively so the tool works regardless of which is actually returned.
This commit is contained in:
@@ -26,6 +26,7 @@ export const SimilarwebBlock: BlockConfig = {
|
||||
{ label: 'Bounce Rate', id: 'similarweb_bounce_rate' },
|
||||
{ label: 'Pages Per Visit', id: 'similarweb_pages_per_visit' },
|
||||
{ label: 'Visit Duration (Desktop)', id: 'similarweb_visit_duration' },
|
||||
{ label: 'Page Views', id: 'similarweb_page_views' },
|
||||
],
|
||||
value: () => 'similarweb_website_overview',
|
||||
},
|
||||
@@ -88,6 +89,7 @@ export const SimilarwebBlock: BlockConfig = {
|
||||
title: 'Start Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM (e.g., 2024-01)',
|
||||
mode: 'advanced',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'similarweb_website_overview',
|
||||
@@ -112,6 +114,7 @@ Return ONLY the date string in YYYY-MM format - no explanations, no quotes, no e
|
||||
title: 'End Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM (e.g., 2024-12)',
|
||||
mode: 'advanced',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'similarweb_website_overview',
|
||||
@@ -134,6 +137,7 @@ Return ONLY the date string in YYYY-MM format - no explanations, no quotes, no e
|
||||
id: 'mainDomainOnly',
|
||||
title: 'Main Domain Only',
|
||||
type: 'switch',
|
||||
mode: 'advanced',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'similarweb_website_overview',
|
||||
@@ -157,6 +161,7 @@ Return ONLY the date string in YYYY-MM format - no explanations, no quotes, no e
|
||||
'similarweb_bounce_rate',
|
||||
'similarweb_pages_per_visit',
|
||||
'similarweb_visit_duration',
|
||||
'similarweb_page_views',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => params.operation,
|
||||
@@ -197,6 +202,7 @@ Return ONLY the date string in YYYY-MM format - no explanations, no quotes, no e
|
||||
bounceRate: { type: 'json', description: 'Bounce rate data over time' },
|
||||
pagesPerVisit: { type: 'json', description: 'Pages per visit data over time' },
|
||||
averageVisitDuration: { type: 'json', description: 'Desktop visit duration data over time' },
|
||||
pageViews: { type: 'json', description: 'Page view data over time' },
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -3198,6 +3198,7 @@ import {
|
||||
import {
|
||||
similarwebBounceRateTool,
|
||||
similarwebPagesPerVisitTool,
|
||||
similarwebPageViewsTool,
|
||||
similarwebTrafficVisitsTool,
|
||||
similarwebVisitDurationTool,
|
||||
similarwebWebsiteOverviewTool,
|
||||
@@ -5166,6 +5167,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
similarweb_bounce_rate: similarwebBounceRateTool,
|
||||
similarweb_pages_per_visit: similarwebPagesPerVisitTool,
|
||||
similarweb_visit_duration: similarwebVisitDurationTool,
|
||||
similarweb_page_views: similarwebPageViewsTool,
|
||||
servicenow_create_record: servicenowCreateRecordTool,
|
||||
servicenow_read_record: servicenowReadRecordTool,
|
||||
servicenow_update_record: servicenowUpdateRecordTool,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export { similarwebBounceRateTool } from './bounce_rate'
|
||||
export { similarwebPageViewsTool } from './page_views'
|
||||
export { similarwebPagesPerVisitTool } from './pages_per_visit'
|
||||
export { similarwebTrafficVisitsTool } from './traffic_visits'
|
||||
export * from './types'
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import type {
|
||||
SimilarwebPageViewsParams,
|
||||
SimilarwebPageViewsResponse,
|
||||
} from '@/tools/similarweb/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const similarwebPageViewsTool: ToolConfig<
|
||||
SimilarwebPageViewsParams,
|
||||
SimilarwebPageViewsResponse
|
||||
> = {
|
||||
id: 'similarweb_page_views',
|
||||
name: 'SimilarWeb Page Views',
|
||||
description: 'Get total page views over time (desktop and mobile combined)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SimilarWeb API key',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Website domain to analyze (e.g., "example.com" without www or protocol)',
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'2-letter ISO country code (e.g., "us", "gb", "de") or "world" for worldwide data',
|
||||
},
|
||||
granularity: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Data granularity: daily, weekly, or monthly',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start date in YYYY-MM format (e.g., "2024-01")',
|
||||
},
|
||||
endDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End date in YYYY-MM format (e.g., "2024-12")',
|
||||
},
|
||||
mainDomainOnly: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude subdomains from results',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = params.domain
|
||||
?.trim()
|
||||
.replace(/^(https?:\/\/)?(www\.)?/, '')
|
||||
.replace(/\/$/, '')
|
||||
const url = new URL(
|
||||
`https://api.similarweb.com/v1/website/${domain}/total-traffic-and-engagement/page-views`
|
||||
)
|
||||
url.searchParams.set('api_key', params.apiKey?.trim())
|
||||
url.searchParams.set('country', params.country?.trim() ?? 'world')
|
||||
url.searchParams.set('granularity', params.granularity ?? 'monthly')
|
||||
url.searchParams.set('format', 'json')
|
||||
if (params.startDate) url.searchParams.set('start_date', params.startDate)
|
||||
if (params.endDate) url.searchParams.set('end_date', params.endDate)
|
||||
if (params.mainDomainOnly !== undefined)
|
||||
url.searchParams.set('main_domain_only', String(params.mainDomainOnly))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.message || 'Failed to get page views')
|
||||
}
|
||||
|
||||
const meta = data.meta ?? {}
|
||||
const request = meta.request ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
domain: request.domain ?? null,
|
||||
country: request.country ?? null,
|
||||
granularity: request.granularity ?? null,
|
||||
lastUpdated: meta.last_updated ?? null,
|
||||
// SimilarWeb's own docs example response uses "pages_views" (with the extra "s") for
|
||||
// this endpoint, unlike its sibling total-traffic-and-engagement endpoints; fall back
|
||||
// to "page_views" too in case that spelling changes or varies by account.
|
||||
pageViews:
|
||||
(data.pages_views ?? data.page_views)?.map(
|
||||
(p: { date: string; pages_views?: number; page_views?: number }) => ({
|
||||
date: p.date,
|
||||
pageViews: p.pages_views ?? p.page_views ?? 0,
|
||||
})
|
||||
) ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
domain: {
|
||||
type: 'string',
|
||||
description: 'Analyzed domain',
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
description: 'Country filter applied',
|
||||
},
|
||||
granularity: {
|
||||
type: 'string',
|
||||
description: 'Data granularity',
|
||||
},
|
||||
lastUpdated: {
|
||||
type: 'string',
|
||||
description: 'Data last updated timestamp',
|
||||
optional: true,
|
||||
},
|
||||
pageViews: {
|
||||
type: 'array',
|
||||
description: 'Page view data over time',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
date: { type: 'string', description: 'Date (YYYY-MM-DD)' },
|
||||
pageViews: { type: 'number', description: 'Total page views' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -117,6 +117,27 @@ export interface SimilarwebPagesPerVisitResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Page Views parameters
|
||||
*/
|
||||
export interface SimilarwebPageViewsParams extends SimilarwebTimeSeriesParams {}
|
||||
|
||||
/**
|
||||
* Page Views response
|
||||
*/
|
||||
export interface SimilarwebPageViewsResponse extends ToolResponse {
|
||||
output: {
|
||||
domain: string
|
||||
country: string
|
||||
granularity: string
|
||||
lastUpdated: string | null
|
||||
pageViews: Array<{
|
||||
date: string
|
||||
pageViews: number
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Average Visit Duration parameters
|
||||
*/
|
||||
|
||||
@@ -116,7 +116,13 @@ export const similarwebWebsiteOverviewTool: ToolConfig<
|
||||
search: sources.Search ?? sources.search ?? null,
|
||||
social: sources.Social ?? sources.social ?? null,
|
||||
mail: sources.Mail ?? sources.mail ?? null,
|
||||
paidReferrals: sources['Paid Referrals'] ?? sources.paid_referrals ?? null,
|
||||
paidReferrals:
|
||||
sources['Paid Referrals'] ??
|
||||
// SimilarWeb's API Lite response literally uses "paid _referrals" (space before
|
||||
// the underscore) as the key for this field.
|
||||
sources['paid _referrals'] ??
|
||||
sources.paid_referrals ??
|
||||
null,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user