Blogger MCP Server

by niyonabil
Verified
import { google, blogger_v3 } from 'googleapis'; import { BloggerBlog, BloggerPost, BloggerLabel } from './types'; import { config } from './config'; /** * Types personnalisés pour compenser les limitations de l'API Blogger */ interface BloggerLabelList { kind?: string; items?: BloggerLabel[]; } /** * Service d'interaction avec l'API Blogger de Google */ export class BloggerService { private blogger: blogger_v3.Blogger; /** * Initialise le service Blogger avec l'API key */ constructor() { this.blogger = google.blogger({ version: 'v3', auth: config.blogger.apiKey, timeout: config.blogger.timeout }); } /** * Liste tous les blogs accessibles * @returns Liste des blogs */ async listBlogs(): Promise<blogger_v3.Schema$BlogList> { try { const response = await this.blogger.blogs.listByUser({ userId: 'self' }); return response.data; } catch (error) { console.error('Erreur lors de la récupération des blogs:', error); throw error; } } /** * Récupère les détails d'un blog spécifique * @param blogId ID du blog à récupérer * @returns Détails du blog */ async getBlog(blogId: string): Promise<blogger_v3.Schema$Blog> { try { const response = await this.blogger.blogs.get({ blogId }); return response.data; } catch (error) { console.error(`Erreur lors de la récupération du blog ${blogId}:`, error); throw error; } } /** * Simule la création d'un nouveau blog * Note: L'API Blogger ne permet pas réellement de créer un blog via API * Cette méthode simule la fonctionnalité et retourne un message d'erreur explicatif * * @param blogData Données du blog à créer * @returns Message d'erreur explicatif */ async createBlog(blogData: Partial<BloggerBlog>): Promise<any> { // Simuler un délai pour rendre la réponse plus réaliste await new Promise(resolve => setTimeout(resolve, 500)); // Retourner un message d'erreur explicatif return { error: true, message: "L'API Blogger de Google ne permet pas de créer un nouveau blog via API. Veuillez créer un blog manuellement sur blogger.com.", details: "Cette limitation est documentée par Google. Les blogs doivent être créés via l'interface web de Blogger.", suggestedAction: "Créez un blog sur https://www.blogger.com, puis utilisez son ID avec ce serveur MCP." }; } /** * Liste les posts d'un blog * @param blogId ID du blog * @param maxResults Nombre maximum de résultats à retourner * @returns Liste des posts */ async listPosts(blogId: string, maxResults?: number): Promise<blogger_v3.Schema$PostList> { try { const response = await this.blogger.posts.list({ blogId, maxResults: maxResults || config.blogger.maxResults }); return response.data; } catch (error) { console.error(`Erreur lors de la récupération des posts du blog ${blogId}:`, error); throw error; } } /** * Recherche des posts dans un blog * @param blogId ID du blog * @param query Terme de recherche * @param maxResults Nombre maximum de résultats à retourner * @returns Liste des posts correspondants */ async searchPosts(blogId: string, query: string, maxResults?: number): Promise<blogger_v3.Schema$PostList> { try { // Récupérer tous les posts puis filtrer côté client // car l'API Blogger ne fournit pas d'endpoint de recherche directe const response = await this.blogger.posts.list({ blogId, maxResults: maxResults || config.blogger.maxResults }); // Filtrer les posts qui contiennent le terme de recherche const allPosts = response.data.items || []; const filteredPosts = allPosts.filter(post => { const title = post.title?.toLowerCase() || ''; const content = post.content?.toLowerCase() || ''; const searchTerm = query.toLowerCase(); return title.includes(searchTerm) || content.includes(searchTerm); }); return { kind: response.data.kind, items: filteredPosts }; } catch (error) { console.error(`Erreur lors de la recherche de posts dans le blog ${blogId}:`, error); throw error; } } /** * Récupère un post spécifique * @param blogId ID du blog * @param postId ID du post * @returns Détails du post */ async getPost(blogId: string, postId: string): Promise<blogger_v3.Schema$Post> { try { const response = await this.blogger.posts.get({ blogId, postId }); return response.data; } catch (error) { console.error(`Erreur lors de la récupération du post ${postId}:`, error); throw error; } } /** * Crée un nouveau post dans un blog * @param blogId ID du blog * @param postData Données du post à créer * @returns Post créé */ async createPost(blogId: string, postData: Partial<BloggerPost>): Promise<blogger_v3.Schema$Post> { try { const response = await this.blogger.posts.insert({ blogId, requestBody: postData as blogger_v3.Schema$Post }); return response.data; } catch (error) { console.error(`Erreur lors de la création du post dans le blog ${blogId}:`, error); throw error; } } /** * Met à jour un post existant * @param blogId ID du blog * @param postId ID du post * @param postData Données du post à mettre à jour * @returns Post mis à jour */ async updatePost(blogId: string, postId: string, postData: Partial<BloggerPost>): Promise<blogger_v3.Schema$Post> { try { // Convertir les types pour éviter les erreurs de compilation const requestBody: blogger_v3.Schema$Post = { title: postData.title, content: postData.content, labels: postData.labels }; const response = await this.blogger.posts.update({ blogId, postId, requestBody }); return response.data; } catch (error) { console.error(`Erreur lors de la mise à jour du post ${postId}:`, error); throw error; } } /** * Supprime un post * @param blogId ID du blog * @param postId ID du post * @returns Statut de la suppression */ async deletePost(blogId: string, postId: string): Promise<void> { try { await this.blogger.posts.delete({ blogId, postId }); } catch (error) { console.error(`Erreur lors de la suppression du post ${postId}:`, error); throw error; } } /** * Liste les labels d'un blog * @param blogId ID du blog * @returns Liste des labels */ async listLabels(blogId: string): Promise<BloggerLabelList> { try { // L'API Blogger ne fournit pas d'endpoint direct pour lister les labels // Nous allons récupérer tous les posts et extraire les labels uniques const response = await this.blogger.posts.list({ blogId, maxResults: 50 // Récupérer un nombre suffisant de posts pour extraire les labels }); const posts = response.data.items || []; const labelSet = new Set<string>(); // Extraire tous les labels uniques des posts posts.forEach(post => { const postLabels = post.labels || []; postLabels.forEach(label => labelSet.add(label)); }); // Convertir en format attendu const labels = Array.from(labelSet).map(name => ({ name })); return { kind: 'blogger#labelList', items: labels }; } catch (error) { console.error(`Erreur lors de la récupération des labels du blog ${blogId}:`, error); throw error; } } /** * Récupère un label spécifique * @param blogId ID du blog * @param labelName Nom du label * @returns Détails du label */ async getLabel(blogId: string, labelName: string): Promise<BloggerLabel> { try { // L'API Blogger ne fournit pas d'endpoint direct pour récupérer un label // Nous allons vérifier si le label existe en listant les labels const labels = await this.listLabels(blogId); const label = labels.items?.find(l => l.name === labelName); if (!label) { throw new Error(`Label ${labelName} non trouvé`); } return label; } catch (error) { console.error(`Erreur lors de la récupération du label ${labelName}:`, error); throw error; } } }