diff --git a/packages/api/src/client/base.ts b/packages/api/src/client/base.ts index 9f5bf81..eec965a 100644 --- a/packages/api/src/client/base.ts +++ b/packages/api/src/client/base.ts @@ -59,6 +59,16 @@ import type { AcquisitionDeal, getOrganizationsParameters, getProjectsParameters, + getInfluencersResponse, + getInfluencerResponse, + getInfluencerMindshareTimeSeriesParameters, + getInfluencerMindshareTimeSeriesResponse, + getInfluencerParameters, + getAssetMindshareTimeSeriesParameters, + getAssetResponse, + getAssetParameters, + getAssetsResponse, + getAssetMindshareTimeSeriesResponse, } from "../types"; import { LogLevel, type Logger, makeConsoleLogger, createFilteredLogger, noOpLogger } from "../logging"; import type { PaginatedResult, RequestOptions, ClientEventMap, ClientEventType, ClientEventHandler } from "./types"; @@ -316,6 +326,19 @@ export interface TokenUnlocksInterface { getEvents(params: getTokenUnlockEventsParameters, options?: RequestOptions): Promise; } +/** + * Interface for the Signal API methods + */ +export interface SignalAPIInterface { + getInfluencers(): Promise; + getInfluencer(params: getInfluencerParameters): Promise; + getInfluencerMindshareTimeSeries(params: getInfluencerMindshareTimeSeriesParameters): Promise; + + getAssets(): Promise; + getAsset(params: getAssetParameters): Promise; + getAssetMindshareTimeSeries(params: getAssetMindshareTimeSeriesParameters): Promise; +} + /** * Abstract base class for the Messari client * Defines the structure and common functionality that all client implementations must provide @@ -371,6 +394,11 @@ export abstract class MessariClientBase { */ // public abstract readonly recaps: RecapsAPIInterface; + /** + * Interface for Signal-related API methods + */ + public abstract readonly signal: SignalAPIInterface; + /** * Logger instance for the client */ diff --git a/packages/api/src/client/client.ts b/packages/api/src/client/client.ts index 2d0e5ef..a8f4a36 100644 --- a/packages/api/src/client/client.ts +++ b/packages/api/src/client/client.ts @@ -31,6 +31,12 @@ import { getTokenUnlockVestingSchedule, getOrganizations, getProjects, + getInfluencers, + getInfluencer, + getInfluencerMindshareTimeSeries, + getAssets, + getAsset, + getAssetMindshareTimeSeries, } from "../types"; import type { createChatCompletionParameters, @@ -93,6 +99,16 @@ import type { getOrganizationsResponse, getProjectsParameters, getProjectsResponse, + getInfluencersResponse, + getInfluencerParameters, + getInfluencerResponse, + getInfluencerMindshareTimeSeriesResponse, + getInfluencerMindshareTimeSeriesParameters, + getAssetMindshareTimeSeriesResponse, + getAssetParameters, + getAssetMindshareTimeSeriesParameters, + getAssetsResponse, + getAssetResponse, } from "../types"; import type { Agent } from "node:http"; import { pick } from "../utils"; @@ -120,6 +136,7 @@ import type { RecapsAPIInterface, ResearchInterface, TokenUnlocksInterface, + SignalAPIInterface, } from "./base"; import { MessariClientBase } from "./base"; @@ -897,6 +914,48 @@ export class MessariClient extends MessariClientBase { }, }; + public readonly signal: SignalAPIInterface = { + getInfluencers: async () => { + return this.request({ + method: getInfluencers.method, + path: getInfluencers.path(), + }); + }, + getInfluencer: async (params: getInfluencerParameters) => { + return this.request({ + method: getInfluencer.method, + path: getInfluencer.path(params), + }); + }, + getInfluencerMindshareTimeSeries: async (params: getInfluencerMindshareTimeSeriesParameters) => { + return this.request({ + method: getInfluencerMindshareTimeSeries.method, + path: getInfluencerMindshareTimeSeries.path(params), + }); + }, + getAssets: async (options?: RequestOptions) => { + return this.request({ + method: getAssets.method, + path: getAssets.path(), + options, + }); + }, + getAsset: async (params: getAssetParameters, options?: RequestOptions) => { + return this.request({ + method: getAsset.method, + path: getAsset.path(params), + options, + }); + }, + getAssetMindshareTimeSeries: async (params: getAssetMindshareTimeSeriesParameters, options?: RequestOptions) => { + return this.request({ + method: getAssetMindshareTimeSeries.method, + path: getAssetMindshareTimeSeries.path(params), + options, + }); + }, + }; + // Recaps is commented out as we don't want to expose it yet // public readonly recaps: RecapsAPIInterface = { // getProjectRecap: async (params: getProjectRecapParameters) => { diff --git a/packages/api/src/types/index.ts b/packages/api/src/types/index.ts index a17f44e..647e004 100644 --- a/packages/api/src/types/index.ts +++ b/packages/api/src/types/index.ts @@ -496,5 +496,95 @@ export const getTokenUnlockEvents = { path: (p: PathParams) => `/token-unlocks/v1/assets/${p.assetId}/events` } as const; + +export type getInfluencersResponse = components['schemas']['Influencer'][]; +export type getInfluencersError = components['schemas']['APIError']; + +export type getInfluencersParameters = { page?: number; pageSize?: number }; + + +export const getInfluencers = { + method: 'GET' as const, + pathParams: [] as const, + queryParams: ['page', 'pageSize'] as const, + bodyParams: [] as const, + path: () => '/signal/v0/influencers' +} as const; + + +export type getInfluencerResponse = components['schemas']['Influencer']; +export type getInfluencerError = components['schemas']['APIError']; + +export type getInfluencerParameters = { influencerId: string }; + + +export const getInfluencer = { + method: 'GET' as const, + pathParams: ['influencerId'] as const, + queryParams: [] as const, + bodyParams: [] as const, + path: (p: PathParams) => `/signal/v0/influencers/${p.influencerId}` +} as const; + + +export type getInfluencerMindshareTimeSeriesResponse = components['schemas']['TimeseriesData']; +export type getInfluencerMindshareTimeSeriesError = components['schemas']['APIError']; + +export type getInfluencerMindshareTimeSeriesParameters = { start?: string; end?: string; granularity?: string } & { influencerId: string }; + + +export const getInfluencerMindshareTimeSeries = { + method: 'GET' as const, + pathParams: ['influencerId'] as const, + queryParams: ['start', 'end', 'granularity'] as const, + bodyParams: [] as const, + path: (p: PathParams) => `/signal/v0/influencers/${p.influencerId}/time-series/mindshare/${p.granularity}` +} as const; + + +export type getAssetsResponse = components['schemas']['SignalAsset'][]; +export type getAssetsError = components['schemas']['APIError']; + +export type getAssetsParameters = { page?: number; pageSize?: number }; + + +export const getAssets = { + method: 'GET' as const, + pathParams: [] as const, + queryParams: ['page', 'pageSize'] as const, + bodyParams: [] as const, + path: () => '/signal/v0/assets' +} as const; + + +export type getAssetResponse = components['schemas']['SignalAsset']; +export type getAssetError = components['schemas']['APIError']; + +export type getAssetParameters = { assetId: string }; + + +export const getAsset = { + method: 'GET' as const, + pathParams: ['assetId'] as const, + queryParams: [] as const, + bodyParams: [] as const, + path: (p: PathParams) => `/signal/v0/assets/${p.assetId}` +} as const; + + +export type getAssetMindshareTimeSeriesResponse = components['schemas']['TimeseriesData']; +export type getAssetMindshareTimeSeriesError = components['schemas']['APIError']; + +export type getAssetMindshareTimeSeriesParameters = { start?: string; end?: string } & { assetId: string; granularity: string }; + + +export const getAssetMindshareTimeSeries = { + method: 'GET' as const, + pathParams: ['assetId', 'granularity'] as const, + queryParams: ['start', 'end'] as const, + bodyParams: [] as const, + path: (p: PathParams) => `/signal/v0/assets/${p.assetId}/time-series/mindshare/${p.granularity}` +} as const; + // Re-export schema types export * from './schema'; diff --git a/packages/api/src/types/schema.ts b/packages/api/src/types/schema.ts index 40ba0b7..08a368b 100644 --- a/packages/api/src/types/schema.ts +++ b/packages/api/src/types/schema.ts @@ -97,6 +97,8 @@ export type GetProjectRecapResponse = components['schemas']['GetProjectRecapResp export type GroupedEntity = components['schemas']['GroupedEntity']; +export type Influencer = components['schemas']['Influencer']; + export type IntelResponse = components['schemas']['IntelResponse']; export type Investors = components['schemas']['Investors']; @@ -117,6 +119,10 @@ export type Person = components['schemas']['Person']; export type PlatformContract = components['schemas']['PlatformContract']; +export type Point = components['schemas']['Point']; + +export type PointSchema = components['schemas']['PointSchema']; + export type Project = components['schemas']['Project']; export type ProjectRecapResponse = components['schemas']['ProjectRecapResponse']; @@ -139,6 +145,10 @@ export type Resource = components['schemas']['Resource']; export type SelectedEntity = components['schemas']['SelectedEntity']; +export type SignalAsset = components['schemas']['SignalAsset']; + +export type SnapshotListingMetadata = components['schemas']['SnapshotListingMetadata']; + export type Source = components['schemas']['Source']; export type SourceList = components['schemas']['SourceList']; @@ -151,6 +161,10 @@ export type Tag = components['schemas']['Tag']; export type TimeUTC = components['schemas']['TimeUTC']; +export type TimeseriesData = components['schemas']['TimeseriesData']; + +export type TimeseriesMetadata = components['schemas']['TimeseriesMetadata']; + export type TokenUnlockAllocation = components['schemas']['TokenUnlockAllocation']; export type TokenUnlockData = components['schemas']['TokenUnlockData']; diff --git a/packages/api/src/types/types.ts b/packages/api/src/types/types.ts index 56ce134..14cd095 100644 --- a/packages/api/src/types/types.ts +++ b/packages/api/src/types/types.ts @@ -204,6 +204,48 @@ export type paths = { */ get: operations["getResearchReportTags"]; }; + "/signal/v0/assets": { + /** + * Get Assets + * @description Get a list of crypto assets. + */ + get: operations["getAssets"]; + }; + "/signal/v0/assets/{assetId}": { + /** + * Get Asset + * @description Get a specific crypto asset by ID. + */ + get: operations["getAsset"]; + }; + "/signal/v0/assets/{assetId}/time-series/mindshare/{granularity}": { + /** + * Get asset mindshare time series + * @description Get the mindshare time series for a specific crypto asset. + */ + get: operations["getAssetMindshareTimeSeries"]; + }; + "/signal/v0/influencers": { + /** + * Get Influencers + * @description Get a list of crypto influencers. + */ + get: operations["getInfluencers"]; + }; + "/signal/v0/influencers/{influencerId}": { + /** + * Get individual influencer + * @description Get a specific crypto influencer by ID. + */ + get: operations["getInfluencer"]; + }; + "/signal/v0/influencers/{influencerId}/time-series/mindshare/{granularity}": { + /** + * Get influencer mindshare time series + * @description Get the mindshare time series for a specific crypto influencer. + */ + get: operations["getInfluencerMindshareTimeSeries"]; + }; "/token-unlocks/v1/allocations": { /** * Get token unlock allocations @@ -833,6 +875,50 @@ export type components = { /** @description List of similar entities found */ similarEntities?: components["schemas"]["Entity"][]; }; + Influencer: { + /** @description Bio or description of the influencer */ + description?: string; + /** @description Unique identifier for the influencer */ + id?: string; + /** @description Location of the influencer */ + location?: string; + mindshare?: { + /** + * Format: double + * @description Latest mindshare score + */ + latestScore?: number; + /** + * Format: double + * @description 24-hour mindshare score change + */ + scoreChange1d?: number; + /** + * Format: double + * @description 7-day mindshare score change + */ + scoreChange7d?: number; + /** + * Format: double + * @description 30-day mindshare score change + */ + scoreChange30d?: number; + }; + /** @description Display name of the influencer */ + name?: string; + /** @description URL to the influencer's profile image */ + profileImageUrl?: string; + socialMetrics?: { + /** @description Number of followers */ + followersCount?: number; + /** @description Number of accounts being followed */ + followingCount?: number; + /** @description Total number of tweets */ + tweetCount?: number; + }; + /** @description Username of the influencer */ + username?: string; + }; /** @description Intel information response */ IntelResponse: { metadata?: { @@ -946,6 +1032,15 @@ export type components = { contractAddress?: string; platform?: string; }; + Point: { + [key: string]: number; + }; + PointSchema: { + description?: string; + isTimestamp?: boolean; + name?: string; + slug?: string; + }; Project: { /** @description Category of the project */ category?: string; @@ -1164,6 +1259,50 @@ export type components = { name?: string; relevanceScore?: string; }; + SignalAsset: { + /** @description Unique identifier for the asset */ + id?: string; + mindshare?: { + /** + * Format: double + * @description Latest mindshare score + */ + latestScore?: number; + /** @description Mindshare rank of the asset */ + rank?: number; + /** + * Format: double + * @description 24-hour mindshare score change + */ + scoreChange1d?: number; + /** + * Format: double + * @description 7-day mindshare score change + */ + scoreChange7d?: number; + /** + * Format: double + * @description 30-day mindshare score change + */ + scoreChange30d?: number; + }; + /** @description Name of the asset */ + name?: string; + /** @description Slug identifier of the asset */ + slug?: string; + /** @description Symbol of the asset */ + symbol?: string; + }; + SnapshotListingMetadata: { + /** @description Current page number */ + page?: number; + /** @description Number of items per page */ + pageSize?: number; + /** @description Total number of pages */ + totalPages?: number; + /** @description Total number of items */ + totalRows?: number; + }; Source: { /** * Format: uuid @@ -1192,6 +1331,14 @@ export type components = { /** @description Name of the tag */ name: string; }; + TimeseriesData: { + points?: components["schemas"]["Point"][]; + }; + TimeseriesMetadata: { + /** @enum {string} */ + granularity?: "1d" | "1h"; + pointSchemas?: components["schemas"]["PointSchema"][]; + }; /** * Format: date-time * @description UTC timestamp @@ -2459,6 +2606,246 @@ export type operations = { }; }; }; + /** + * Get Assets + * @description Get a list of crypto assets. + */ + getAssets: { + parameters: { + query?: { + /** @description Page number for pagination */ + page?: number; + /** @description Number of items per page (max 2000) */ + pageSize?: number; + }; + }; + responses: { + /** @description Successful response */ + 200: { + content: { + "application/json": components["schemas"]["APIResponseWithMetadata"] & { + data?: components["schemas"]["SignalAsset"][]; + metadata?: components["schemas"]["SnapshotListingMetadata"]; + }; + }; + }; + /** @description Client error response */ + 400: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Server error response */ + 500: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; + /** + * Get Asset + * @description Get a specific crypto asset by ID. + */ + getAsset: { + parameters: { + path: { + /** @description Asset ID (either a slug or UUID) */ + assetId: string; + }; + }; + responses: { + /** @description Successful response */ + 200: { + content: { + "application/json": components["schemas"]["APIResponse"] & { + data?: components["schemas"]["SignalAsset"]; + }; + }; + }; + /** @description Client error response */ + 400: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Asset not found */ + 404: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Server error response */ + 500: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; + /** + * Get asset mindshare time series + * @description Get the mindshare time series for a specific crypto asset. + */ + getAssetMindshareTimeSeries: { + parameters: { + query?: { + /** @description Time range start */ + start?: string; + /** @description Time range end */ + end?: string; + }; + path: { + /** @description Asset ID (either a slug or UUID) */ + assetId: string; + /** @description Time series granularity */ + granularity: "1d" | "1h"; + }; + }; + responses: { + /** @description Successful response */ + 200: { + content: { + "application/json": components["schemas"]["APIResponseWithMetadata"] & { + data?: components["schemas"]["TimeseriesData"]; + metadata?: components["schemas"]["TimeseriesMetadata"]; + }; + }; + }; + /** @description Client error response */ + 400: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Asset not found */ + 404: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Server error response */ + 500: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; + /** + * Get Influencers + * @description Get a list of crypto influencers. + */ + getInfluencers: { + parameters: { + query?: { + /** @description Page number for pagination */ + page?: number; + /** @description Number of items per page (max 200) */ + pageSize?: number; + }; + }; + responses: { + /** @description Successful response */ + 200: { + content: { + "application/json": components["schemas"]["APIResponseWithMetadata"] & { + data?: components["schemas"]["Influencer"][]; + metadata?: components["schemas"]["SnapshotListingMetadata"]; + }; + }; + }; + /** @description Client error response */ + 400: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Server error response */ + 500: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; + /** + * Get individual influencer + * @description Get a specific crypto influencer by ID. + */ + getInfluencer: { + parameters: { + path: { + /** @description Influencer ID (either a numeric X User ID or a username like '@handle') */ + influencerId: string; + }; + }; + responses: { + /** @description Successful response */ + 200: { + content: { + "application/json": components["schemas"]["APIResponse"] & { + data?: components["schemas"]["Influencer"]; + }; + }; + }; + /** @description Client error response */ + 400: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Server error response */ + 500: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; + /** + * Get influencer mindshare time series + * @description Get the mindshare time series for a specific crypto influencer. + */ + getInfluencerMindshareTimeSeries: { + parameters: { + query?: { + /** @description Time range start */ + start?: string; + /** @description Time range end */ + end?: string; + /** @description Time series granularity */ + granularity?: "1d" | "1h"; + }; + path: { + /** @description Influencer ID (either a numeric X User ID or a username like '@handle') */ + influencerId: string; + }; + }; + responses: { + /** @description Successful response */ + 200: { + content: { + "application/json": components["schemas"]["APIResponseWithMetadata"] & { + data?: components["schemas"]["TimeseriesData"]; + metadata?: components["schemas"]["TimeseriesMetadata"]; + }; + }; + }; + /** @description Client error response */ + 400: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + /** @description Server error response */ + 500: { + content: { + "application/json": components["schemas"]["APIError"]; + }; + }; + }; + }; /** * Get token unlock allocations * @description Returns allocation information given a set of asset IDs diff --git a/packages/examples/package.json b/packages/examples/package.json index 79a0953..267c214 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -16,7 +16,8 @@ "start:news": "tsx src/newsFeed.ts", "start:recaps": "tsx src/recaps.ts", "start:research": "tsx src/research.ts", - "start:token-unlocks": "tsx src/tokenUnlocks.ts" + "start:token-unlocks": "tsx src/tokenUnlocks.ts", + "start:signal": "tsx src/signal.ts" }, "dependencies": { "@messari/sdk": "workspace:*", diff --git a/packages/examples/src/signal.ts b/packages/examples/src/signal.ts new file mode 100644 index 0000000..a914e3c --- /dev/null +++ b/packages/examples/src/signal.ts @@ -0,0 +1,77 @@ +import { MessariClient } from "@messari/sdk"; +import { printTable } from "console-table-printer"; +import dotenv from "dotenv"; + +// Load environment variables from .env file +dotenv.config(); + +// Get API key from environment variable +const API_KEY = process.env.MESSARI_API_KEY; + +// Check if API key is available +if (!API_KEY) { + console.error("Error: MESSARI_API_KEY environment variable is not set."); + console.error("Please create a .env file with your API key or set it in your environment."); + process.exit(1); +} + +// Initialize the Messari client +const client = new MessariClient({ + apiKey: API_KEY, +}); + +async function main() { + try { + console.log("\n--------------------------------"); + console.log("Twitter Influencer Mindshare"); + console.log("--------------------------------"); + const influencersResponse = await client.signal.getInfluencers(); + const rows = []; + + for (const influencer of influencersResponse) { + if (rows.length > 5) break; + if (!influencer.socialMetrics || !influencer.mindshare) continue; + rows.push({ + "Id": influencer.id, + "Username": influencer.username, + "Followers": influencer.socialMetrics.followersCount, + "Mindshare": formatScore(influencer.mindshare.latestScore), + "7DChange": formatScore(influencer.mindshare.scoreChange7d), + "30DChange": formatScore(influencer.mindshare.scoreChange30d), + }); + } + printTable(rows); + } catch (error) { + console.error("Error calling getInfluencers:", error); + } + try { + console.log("\n--------------------------------"); + console.log("Twitter Asset Mindshare"); + console.log("--------------------------------"); + const assetsResponse = await client.signal.getAssets(); + const rows = []; + + for (const asset of assetsResponse) { + if (rows.length > 5) break; + if (!asset.mindshare) continue; + rows.push({ + "Id": asset.id, + "Name": asset.name, + "Symbol": asset.symbol, + "Mindshare": formatScore(asset.mindshare.latestScore), + "7DChange": formatScore(asset.mindshare.scoreChange7d), + "30DChange": formatScore(asset.mindshare.scoreChange30d), + }); + } + printTable(rows); + } catch (error) { + console.error("Error calling getAssets:", error); + } +} + +function formatScore(score: number | undefined) { + if (!score) return "-"; + return score.toFixed(2); +} + +main().catch(console.error); diff --git a/typegen/openapi/index.yaml b/typegen/openapi/index.yaml index 7560500..b2590d0 100644 --- a/typegen/openapi/index.yaml +++ b/typegen/openapi/index.yaml @@ -138,3 +138,16 @@ paths: /token-unlocks/v1/assets/{assetId}/events: $ref: "./services/token-unlocks/openapi.yaml#/paths/~1token-unlocks~1v1~1assets~1{assetId}~1events" + # Signal Service Paths + /signal/v0/influencers: + $ref: "./services/signal/openapi.yaml#/paths/~1signal~1v0~1influencers" + /signal/v0/influencers/{influencerId}: + $ref: "./services/signal/openapi.yaml#/paths/~1signal~1v0~1influencers~1{influencerId}" + /signal/v0/influencers/{influencerId}/time-series/mindshare/{granularity}: + $ref: "./services/signal/openapi.yaml#/paths/~1signal~1v0~1influencers~1{influencerId}~1time-series~1mindshare~1{granularity}" + /signal/v0/assets: + $ref: "./services/signal/openapi.yaml#/paths/~1signal~1v0~1assets" + /signal/v0/assets/{assetId}: + $ref: "./services/signal/openapi.yaml#/paths/~1signal~1v0~1assets~1{assetId}" + /signal/v0/assets/{assetId}/time-series/mindshare/{granularity}: + $ref: "./services/signal/openapi.yaml#/paths/~1signal~1v0~1assets~1{assetId}~1time-series~1mindshare~1{granularity}" diff --git a/typegen/openapi/services/signal/openapi.yaml b/typegen/openapi/services/signal/openapi.yaml new file mode 100644 index 0000000..f365fe5 --- /dev/null +++ b/typegen/openapi/services/signal/openapi.yaml @@ -0,0 +1,512 @@ +openapi: 3.1.0 +info: + title: Messari Signal API + description: | + API for Signal services tracking crypto influencers and assets and their mindshare over time. + version: 0.0.1 + contact: + name: Messari Engineering + url: https://messari.io + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: https://api.messari.io/signal + description: Production server + - url: https://api.staging.messari.io/signal + description: Staging server + - url: http://localhost:8080/signal + description: Local development server + +tags: + - name: signal:Influencers + description: Crypto influencers endpoints + - name: signal:Assets + description: Crypto assets endpoints + +paths: + /signal/v0/influencers: + get: + operationId: getInfluencers + summary: Get Influencers + description: | + Get a list of crypto influencers. + tags: + - signal:Influencers + security: + - apiKey: [] + parameters: + - name: page + in: query + description: Page number for pagination + required: false + schema: + type: integer + default: 1 + - name: pageSize + in: query + description: Number of items per page (max 200) + required: false + schema: + type: integer + default: 200 + maximum: 200 + responses: + '200': + description: Successful response + content: + application/json: + schema: + allOf: + - $ref: '../../common/components.yaml#/components/schemas/APIResponseWithMetadata' + - type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Influencer' + metadata: + $ref: '#/components/schemas/SnapshotListingMetadata' + '400': + description: Client error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '500': + description: Server error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + + /signal/v0/influencers/{influencerId}: + get: + operationId: getInfluencer + summary: Get individual influencer + description: | + Get a specific crypto influencer by ID. + tags: + - signal:Influencers + security: + - apiKey: [] + parameters: + - name: influencerId + in: path + description: Influencer ID (either a numeric X User ID or a username like '@handle') + required: true + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + allOf: + - $ref: '../../common/components.yaml#/components/schemas/APIResponse' + - type: object + properties: + data: + $ref: '#/components/schemas/Influencer' + '400': + description: Client error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '500': + description: Server error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + + /signal/v0/influencers/{influencerId}/time-series/mindshare/{granularity}: + get: + operationId: getInfluencerMindshareTimeSeries + summary: Get influencer mindshare time series + description: | + Get the mindshare time series for a specific crypto influencer. + tags: + - signal:Influencers + security: + - apiKey: [] + parameters: + - name: influencerId + in: path + description: Influencer ID (either a numeric X User ID or a username like '@handle') + required: true + schema: + type: string + - name: start + in: query + description: Time range start + required: false + schema: + type: string + format: date-time + - name: end + in: query + description: Time range end + required: false + schema: + type: string + format: date-time + - name: granularity + in: query + description: Time series granularity + required: false + schema: + type: string + enum: [1d, 1h] + default: 1d + responses: + '200': + description: Successful response + content: + application/json: + schema: + allOf: + - $ref: '../../common/components.yaml#/components/schemas/APIResponseWithMetadata' + - type: object + properties: + data: + $ref: '#/components/schemas/TimeseriesData' + metadata: + $ref: '#/components/schemas/TimeseriesMetadata' + '400': + description: Client error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '500': + description: Server error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + + /signal/v0/assets: + get: + operationId: getAssets + summary: Get Assets + description: | + Get a list of crypto assets. + tags: + - signal:Assets + security: + - apiKey: [] + parameters: + - name: page + in: query + description: Page number for pagination + required: false + schema: + type: integer + default: 1 + - name: pageSize + in: query + description: Number of items per page (max 2000) + required: false + schema: + type: integer + default: 2000 + maximum: 2000 + responses: + '200': + description: Successful response + content: + application/json: + schema: + allOf: + - $ref: '../../common/components.yaml#/components/schemas/APIResponseWithMetadata' + - type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/SignalAsset' + metadata: + $ref: '#/components/schemas/SnapshotListingMetadata' + '400': + description: Client error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '500': + description: Server error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + + /signal/v0/assets/{assetId}: + get: + operationId: getAsset + summary: Get Asset + description: | + Get a specific crypto asset by ID. + tags: + - signal:Assets + security: + - apiKey: [] + parameters: + - name: assetId + in: path + description: Asset ID (either a slug or UUID) + required: true + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + allOf: + - $ref: '../../common/components.yaml#/components/schemas/APIResponse' + - type: object + properties: + data: + $ref: '#/components/schemas/SignalAsset' + '400': + description: Client error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '404': + description: Asset not found + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '500': + description: Server error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + + /signal/v0/assets/{assetId}/time-series/mindshare/{granularity}: + get: + operationId: getAssetMindshareTimeSeries + summary: Get asset mindshare time series + description: | + Get the mindshare time series for a specific crypto asset. + tags: + - signal:Assets + security: + - apiKey: [] + parameters: + - name: assetId + in: path + description: Asset ID (either a slug or UUID) + required: true + schema: + type: string + - name: granularity + in: path + description: Time series granularity + required: true + schema: + type: string + enum: [1d, 1h] + - name: start + in: query + description: Time range start + required: false + schema: + type: string + format: date-time + - name: end + in: query + description: Time range end + required: false + schema: + type: string + format: date-time + responses: + '200': + description: Successful response + content: + application/json: + schema: + allOf: + - $ref: '../../common/components.yaml#/components/schemas/APIResponseWithMetadata' + - type: object + properties: + data: + $ref: '#/components/schemas/TimeseriesData' + metadata: + $ref: '#/components/schemas/TimeseriesMetadata' + '400': + description: Client error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '404': + description: Asset not found + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + '500': + description: Server error response + content: + application/json: + schema: + $ref: '../../common/components.yaml#/components/schemas/APIError' + +components: + schemas: + Influencer: + type: object + properties: + id: + type: string + description: Unique identifier for the influencer + name: + type: string + description: Display name of the influencer + username: + type: string + description: Username of the influencer + description: + type: string + description: Bio or description of the influencer + location: + type: string + description: Location of the influencer + profileImageUrl: + type: string + description: URL to the influencer's profile image + mindshare: + type: object + properties: + latestScore: + type: number + format: double + description: Latest mindshare score + scoreChange1d: + type: number + format: double + description: 24-hour mindshare score change + scoreChange7d: + type: number + format: double + description: 7-day mindshare score change + scoreChange30d: + type: number + format: double + description: 30-day mindshare score change + socialMetrics: + type: object + properties: + followersCount: + type: integer + description: Number of followers + followingCount: + type: integer + description: Number of accounts being followed + tweetCount: + type: integer + description: Total number of tweets + + TimeseriesData: + type: object + properties: + points: + type: array + items: + $ref: '#/components/schemas/Point' + + Point: + type: object + additionalProperties: + type: number + + TimeseriesMetadata: + type: object + properties: + pointSchemas: + type: array + items: + $ref: '#/components/schemas/PointSchema' + granularity: + type: string + enum: [1d, 1h] + + PointSchema: + type: object + properties: + name: + type: string + slug: + type: string + description: + type: string + isTimestamp: + type: boolean + + SnapshotListingMetadata: + type: object + properties: + pageSize: + type: integer + description: Number of items per page + page: + type: integer + description: Current page number + totalRows: + type: integer + description: Total number of items + totalPages: + type: integer + description: Total number of pages + + SignalAsset: + type: object + properties: + id: + type: string + description: Unique identifier for the asset + name: + type: string + description: Name of the asset + symbol: + type: string + description: Symbol of the asset + slug: + type: string + description: Slug identifier of the asset + mindshare: + type: object + properties: + rank: + type: integer + description: Mindshare rank of the asset + latestScore: + type: number + format: double + description: Latest mindshare score + scoreChange1d: + type: number + format: double + description: 24-hour mindshare score change + scoreChange7d: + type: number + format: double + description: 7-day mindshare score change + scoreChange30d: + type: number + format: double + description: 30-day mindshare score change + +security: + - ApiKeyAuth: [] \ No newline at end of file diff --git a/typegen/scripts/generate-types.sh b/typegen/scripts/generate-types.sh index ff69902..61be378 100755 --- a/typegen/scripts/generate-types.sh +++ b/typegen/scripts/generate-types.sh @@ -25,8 +25,10 @@ if [ -f "typegen/openapi/dist/combined.yaml" ]; then --root-types \ --root-types-no-schema-prefix \ --alphabetize \ - --enum-values \ # Generate true TS enums rather than string unions. - --path-params-as-types false # Leave false to avoid path name collisions with dynamic /{id} and /static paths + --enum-values \ + --path-params-as-types false + # Generate true TS enums rather than string unions. + # Leave path-params-as-types false to avoid path name collisions with dynamic /{id} and /static paths echo "Combined type generation complete!" else