Skip to content
← Public packages

@kentcdodds/groupme

GroupMe REST API helpers for listing groups, reading messages, ranking activity, and preparing relevance-filtered digests for Kody agents.

src/types.ts

306 lines · 7.9 KB · TypeScript
/**
 * Shared TypeScript types for the GroupMe v3 REST API and this package's helpers.
 */

/** GroupMe API response envelope metadata. */
export type GroupMeMeta = {
	code: number
	errors?: string[] | null
}

/** Standard GroupMe API response wrapper. */
export type GroupMeEnvelope<T> = {
	meta: GroupMeMeta
	response: T | null
}

/** Attachment kinds returned on GroupMe messages. */
export type GroupMeAttachmentType =
	| 'image'
	| 'reply'
	| 'mentions'
	| 'location'
	| 'video'
	| 'emoji'
	| string

/** Message attachment payload. Fields vary by attachment type. */
export type GroupMeAttachment = {
	type: GroupMeAttachmentType
	url?: string
	name?: string
	chars?: Array<{ placeholder: string; charmap: string[][] }>
	source_url?: string
	[key: string]: unknown
}

/** Compact preview of the latest group message. */
export type GroupMeMessagePreview = {
	nickname: string
	text: string
	image_url: string
	attachments: GroupMeAttachment[]
}

/** Message count and preview metadata on a group list item. */
export type GroupMeGroupMessagesMeta = {
	count: number
	last_message_id: string
	last_message_created_at: number
	last_message_updated_at: number
	preview: GroupMeMessagePreview
}

/** Group list item returned by `GET /groups`. */
export type GroupMeGroup = {
	id: string
	group_id: string
	name: string
	description: string
	image_url: string | null
	type: string
	phone_number?: string
	creator_user_id?: string
	created_at: number
	updated_at: number
	messages: GroupMeGroupMessagesMeta
	[key: string]: unknown
}

/** Message returned by `GET /groups/:group_id/messages`. */
export type GroupMeMessage = {
	id: string
	group_id: string
	text: string
	name: string
	avatar_url: string
	created_at: number
	user_id: string
	sender_id: string
	sender_type: string
	system: boolean
	attachments: GroupMeAttachment[]
	[key: string]: unknown
}

/** Authenticated user returned by `GET /users/me`. */
export type GroupMeUser = {
	id: string
	name: string
	email?: string
	avatar_url?: string
	[key: string]: unknown
}

/** HTTP method accepted by the low-level request helper. */
export type GroupMeHttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'

/** Query parameters supported by the low-level request helper. */
export type GroupMeQuery = Record<
	string,
	string | number | boolean | null | undefined | Array<string | number>
>

/** Options for the low-level GroupMe request helper. */
export type GroupMeRequestOptions = {
	/** API path such as `/groups` or full `https://api.groupme.com/v3/groups`. */
	path: string
	method?: GroupMeHttpMethod
	query?: GroupMeQuery
	body?: unknown
	/** Throw `GroupMeRequestError` when the HTTP response is not ok. Default true. */
	throwOnError?: boolean
	headers?: Record<string, string>
}

/** Parsed response from the low-level request helper. */
export type GroupMeRequestResponse<T = unknown> = {
	url: string
	ok: boolean
	status: number
	statusText: string
	data: GroupMeEnvelope<T> | T | null
	text: string
}

/** Pagination options for message listing. */
export type GroupMeMessagePagination = {
	/** Page size. GroupMe allows up to 100. Default 100. */
	limit?: number
	/** Return messages immediately before this message id. */
	beforeId?: string
	/** Return messages immediately after this message id. */
	afterId?: string
	/** Return the most recent messages created after this message id. */
	sinceId?: string
}

/** Options for listing groups. */
export type GroupMeListGroupsOptions = {
	/** Skip member lists for faster responses. Default true. */
	omitMemberships?: boolean
	/** Page size. Default 100. */
	limit?: number
	/** Pagination page number. */
	page?: number
}

/** Options for listing messages in one group. */
export type GroupMeListMessagesOptions = GroupMeMessagePagination & {
	groupId: string
}

/** Options for reading messages between instants in a named timezone. */
export type GroupMeMessagesInRangeOptions = {
	groupId: string
	/** Inclusive range start as `YYYY-MM-DD` in `timeZone`, or unix seconds. */
	start: string | number
	/** Exclusive range end as `YYYY-MM-DD` in `timeZone`, or unix seconds. */
	end: string | number
	/**
	 * IANA timezone used when `start`/`end` are date strings.
	 * Default `America/Denver`.
	 */
	timeZone?: string
	/** Safety cap on pages fetched while scanning backward. Default 50. */
	maxPages?: number
}

/** Result of a bounded message scan. */
export type GroupMeMessagesInRangeResult = {
	groupId: string
	timeZone: string
	startUnix: number
	endUnix: number
	messageCount: number
	pagesFetched: number
	messages: GroupMeNormalizedMessage[]
}

/** Group ranked by most recent message activity. */
export type GroupMeActiveGroup = {
	id: string
	name: string
	lastMessageAt: number
	lastMessageAtIso: string
	lastMessagePreview: string
	lastMessageSender: string
	messageCount: number
}

/** Options for ranking groups by recent activity. */
export type GroupMeListMostActiveGroupsOptions = {
	/** Number of groups to return. Default 3. */
	limit?: number
	/** Forwarded to `listGroups`. */
	omitMemberships?: boolean
}

/**
 * Rule-based relevance profile for digest filtering.
 *
 * With no positive criteria (keywords, senderNames, mentions, patterns,
 * attachmentTypes), all messages passing `excludeSystem` and `minTextLength`
 * are kept.
 */
export type GroupMeRelevanceProfile = {
	/** Case-insensitive keyword hits mark a message relevant. */
	keywords?: string[]
	/** Case-insensitive sender-name substrings mark a message relevant. */
	senderNames?: string[]
	/** Case-insensitive @mention or name substrings in message text. */
	mentions?: string[]
	/** Regex source strings; any match marks a message relevant. */
	patterns?: string[]
	/** Drop GroupMe system messages such as joins and edits. Default true. */
	excludeSystem?: boolean
	/** Drop blank or very short messages. Default 4 characters. */
	minTextLength?: number
	/** Attachment types that mark a message relevant, such as `image`. */
	attachmentTypes?: string[]
}

/** Result of relevance filtering. */
export type GroupMeRelevanceFilterResult = {
	inputCount: number
	relevantCount: number
	skippedCount: number
	messages: GroupMeNormalizedMessage[]
}

/** Normalized message shape used by digest helpers. */
export type GroupMeNormalizedMessage = {
	id: string
	groupId: string
	createdAt: number
	createdAtIso: string
	senderName: string
	text: string
	system: boolean
	attachmentTypes: string[]
	attachmentSummary: string
}

/** Options for `prepare-digest`. */
export type GroupMePrepareDigestOptions = {
	/** Group id to read. Provide this or `groupName`. */
	groupId?: string
	/** Case-insensitive exact or substring group-name match. */
	groupName?: string
	start: string | number
	end: string | number
	timeZone?: string
	relevance?: GroupMeRelevanceProfile
	maxPages?: number
}

/** Structured digest input for downstream summarization. */
export type GroupMeDigestInput = {
	group: { id: string; name: string }
	window: {
		timeZone: string
		startUnix: number
		endUnix: number
		startDate: string
		endDate: string
	}
	stats: {
		totalMessages: number
		relevantMessages: number
		skippedMessages: number
		pagesFetched: number
	}
	relevance: GroupMeRelevanceProfile
	messages: GroupMeNormalizedMessage[]
}

/**
 * Return exported GroupMe TypeScript type names for agent discovery.
 * @returns Array of public type name strings.
 * @example
 * import types from 'kody:@kentcdodds/groupme/types'
 * const names = await types()
 * // => ['GroupMeGroup', 'GroupMeMessage', 'GroupMePrepareDigestOptions', ...]
 */
export default function describeGroupMeTypes() {
	return [
		'GroupMeMeta',
		'GroupMeEnvelope',
		'GroupMeAttachment',
		'GroupMeGroup',
		'GroupMeMessage',
		'GroupMeUser',
		'GroupMeRequestOptions',
		'GroupMeRequestResponse',
		'GroupMeListGroupsOptions',
		'GroupMeListMessagesOptions',
		'GroupMeMessagesInRangeOptions',
		'GroupMeMessagesInRangeResult',
		'GroupMeActiveGroup',
		'GroupMeRelevanceProfile',
		'GroupMeRelevanceFilterResult',
		'GroupMeNormalizedMessage',
		'GroupMePrepareDigestOptions',
		'GroupMeDigestInput',
	]
}