Skip to main content
Glama
github.ts5.69 kB
import type { GitHubActivityData, GitHubActivityItem } from "./types.js"; import { cacheOrFetch, CACHE_CONFIGS } from "../utils/cache.js"; import { logger } from "../utils/logger.js"; /** * GitHub Event type for public events API * Replaces @octokit/rest types for lightweight implementation */ interface GitHubEvent { id?: string; type?: string; created_at?: string; repo?: { name?: string; }; payload?: { action?: string; ref_type?: string; commits?: Array<{ message?: string }>; issue?: { title?: string }; pull_request?: { title?: string }; release?: { tag_name?: string; name?: string }; }; } /** * Format a single GitHub event into a structured activity item */ export function formatGitHubEvent( event: GitHubEvent, includeDetails = false, ): GitHubActivityItem { const date = new Date(event.created_at || new Date()).toLocaleDateString(); const repository = event.repo?.name || "Unknown repository"; const payload = event.payload; let action = ""; let details = ""; switch (event.type) { case "PushEvent": { const commits = payload?.commits?.length || 0; action = `Pushed ${commits} commit${commits > 1 ? "s" : ""}`; if (includeDetails && payload?.commits) { const commitMessages = payload.commits .slice(0, 3) .map((c) => ` - ${c.message}`) .join("\n"); details = `\n${commitMessages}`; } break; } case "CreateEvent": { const refType = payload?.ref_type || "repository"; action = `Created ${refType}`; break; } case "IssuesEvent": { const issueAction = payload?.action || "updated"; action = `${issueAction} issue`; if (includeDetails && payload?.issue) { details = `\n - ${payload.issue.title}`; } break; } case "PullRequestEvent": { const prAction = payload?.action || "updated"; action = `${prAction} pull request`; if (includeDetails && payload?.pull_request) { details = `\n - ${payload.pull_request.title}`; } break; } case "WatchEvent": action = "Starred repository"; break; case "ForkEvent": action = "Forked repository"; break; case "ReleaseEvent": { action = `${payload?.action || "created"} release`; if (includeDetails && payload?.release) { details = `\n - ${payload.release.tag_name}: ${payload.release.name}`; } break; } default: action = `${(event.type || "Unknown").replace("Event", "")}`; } return { type: event.type || "Unknown", action, repository, date, details: details || undefined, }; } /** * Fetch GitHub activity data (internal, not cached) * Uses native fetch instead of @octokit/rest for lightweight implementation */ async function fetchGitHubActivityData( limit: number, includeDetails: boolean, ): Promise<GitHubActivityData> { const username = "duyet"; const profileUrl = `https://github.com/${username}`; const apiUrl = `https://api.github.com/users/${username}/events?per_page=${limit}`; try { const startTime = Date.now(); logger.debug("fetch", `Fetching GitHub activity for ${username}`, { limit, includeDetails, }); const response = await fetch(apiUrl, { headers: { Accept: "application/vnd.github.v3+json", "User-Agent": "DuyetMCP/0.1 (+https://duyet.net)", }, }); const duration = Date.now() - startTime; if (!response.ok) { logger.fetch(apiUrl, response.status, duration); throw new Error( `GitHub API returned ${response.status}: ${response.statusText}`, ); } const events: GitHubEvent[] = await response.json(); logger.fetch(apiUrl, response.status, duration); if (events.length === 0) { logger.debug("fetch", "No GitHub events found", { username }); return { activities: [], totalRetrieved: 0, profileUrl, username, }; } const activities = events.map((event) => formatGitHubEvent(event, includeDetails), ); logger.debug("fetch", `Retrieved ${activities.length} GitHub activities`, { username, count: activities.length, }); return { activities, totalRetrieved: activities.length, profileUrl, username, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; logger.error("fetch", `GitHub API error: ${errorMessage}`, { username }); throw new Error(`Error fetching GitHub activity: ${errorMessage}`); } } /** * Get GitHub activity data with caching (15 minutes TTL) * This is the public API that should be used by tools/resources */ export async function getGitHubActivityData( limit = 5, includeDetails = false, ): Promise<GitHubActivityData> { const limitNum = Math.min(Math.max(limit, 1), 20); const cacheKey = `github-activity-${limitNum}-${includeDetails}`; return cacheOrFetch(cacheKey, CACHE_CONFIGS.GITHUB, () => fetchGitHubActivityData(limitNum, includeDetails), ); } /** * Format GitHub activity data for display */ export function formatGitHubActivityForDisplay( data: GitHubActivityData, ): string { if (data.activities.length === 0) { return "No recent GitHub activity found."; } const activityList = data.activities .map( (activity) => `${activity.action} in ${activity.repository} (${activity.date})${activity.details || ""}`, ) .join("\n\n"); return `Recent GitHub Activity for ${data.username}: ${activityList} GitHub Profile: ${data.profileUrl}`; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/duyet/duyet-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server