Publicar en Meta, instagram vamos
A continuación se presenta un ejemplo de código en C# que muestra la arquitectura y la implementación básica para la integración con la API de Meta (Instagram Graph API) dentro de un contexto DDD con AspNetBoilerplate. Este código es ilustrativo y no es un proyecto completo, sino que muestra las clases y métodos clave. En un entorno real, deberás:
- Ajustar espacios de nombres (namespaces).
- Integrar con la infraestructura real del proyecto (repositorios, entidades DDD, módulos de AspNetBoilerplate).
- Configurar la obtención del
AccessToken
inicial (OAuth flow) fuera de este ejemplo. - Agregar manejo de errores, logging, pruebas unitarias y otras buenas prácticas.
Dependencias y Configuración Previa
.NET 5 o superior.
Paquetes NuGet:
Newtonsoft.Json
para deserializar respuestas JSON.Microsoft.Extensions.Http
(opcional) para configurarHttpClient
.
Se asume que ya se cuenta con un AccessToken
de larga duración almacenado en algún repositorio seguro y métodos para refrescarlo.
Interfaces de Dominio
Estas interfaces definen las operaciones que el dominio de redes sociales requiere. Se encuentran en la capa de dominio o aplicación.
// Dominio/Aplicacion/Interfaces/IInstagramApiService.cs
public interface IInstagramApiService
{
Task<InstagramInsightsDto> GetAccountMetricsAsync(string igUserId);
Task<InstagramPostPublishResult> PublishImagePostAsync(string igUserId, string imageUrl, string caption);
Task RefreshAccessTokenAsync(string accountId);
}
// DTOs (podrían ir en un namespace Application.Dto)
public class InstagramInsightsDto
{
public int Impressions { get; set; }
public int Reach { get; set; }
public int ProfileViews { get; set; }
// Agregar otras métricas según necesidad
}
public class InstagramPostPublishResult
{
public string PostId { get; set; }
}
Entidades y Repositorio de Tokens
Se asume la existencia de una entidad SocialMediaAccount
que almacena tokens. Puede ser parte del contexto de dominio "SocialMedia":
// Dominio/Entidades/SocialMediaAccount.cs
public class SocialMediaAccount
{
public Guid Id { get; private set; }
public string Platform { get; private set; } // "Instagram"
public string InstagramUserId { get; private set; } // ID de usuario IG Business
public string AccessToken { get; private set; }
public DateTime AccessTokenExpiration { get; private set; }
// Métodos para actualizar tokens
public void UpdateAccessToken(string newToken, DateTime expiration)
{
AccessToken = newToken;
AccessTokenExpiration = expiration;
}
// Otros atributos y métodos de dominio...
}
// Repositorio
public interface ISocialMediaAccountRepository
{
Task<SocialMediaAccount> GetByInstagramUserIdAsync(string igUserId);
Task<SocialMediaAccount> GetByIdAsync(Guid id);
Task UpdateAsync(SocialMediaAccount account);
}
Implementación del Adaptador para la API de Instagram (Meta)
Este servicio vive en la capa de Infraestructura. Se conecta con la API de Instagram Graph y satisface la interfaz IInstagramApiService
.
// Infraestructura/Servicios/InstagramApiAdapter.cs
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class InstagramApiAdapter : IInstagramApiService
{
private readonly HttpClient _httpClient;
private readonly ISocialMediaAccountRepository _accountRepository;
private readonly string _facebookGraphVersion = "v15.0";
// Asegúrate de usar la versión de la API que corresponda a tu caso.
public InstagramApiAdapter(HttpClient httpClient, ISocialMediaAccountRepository accountRepository)
{
_httpClient = httpClient;
_accountRepository = accountRepository;
}
public async Task<InstagramInsightsDto> GetAccountMetricsAsync(string igUserId)
{
var account = await _accountRepository.GetByInstagramUserIdAsync(igUserId);
if (account == null)
{
throw new Exception("No se encontró la cuenta de Instagram asociada");
}
await EnsureAccessTokenIsValidAsync(account);
var url = $"https://graph.facebook.com/{_facebookGraphVersion}/{igUserId}/insights" +
"?metric=impressions,reach,profile_views&period=day" +
$"&access_token={account.AccessToken}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
// Estructura típica de la respuesta:
// {
// "data": [
// { "name": "impressions", "values": [ { "value": 123 } ], ... },
// { "name": "reach", "values": [ { "value": 234 } ], ... },
// { "name": "profile_views", "values": [ { "value": 10 } ], ... }
// ],
// ...
// }
var insightsRoot = JsonConvert.DeserializeObject<InstagramInsightsRoot>(json);
var result = new InstagramInsightsDto();
if (insightsRoot?.Data != null)
{
foreach (var metric in insightsRoot.Data)
{
var value = metric.Values?[0]?.Value ?? 0;
switch (metric.Name)
{
case "impressions":
result.Impressions = value;
break;
case "reach":
result.Reach = value;
break;
case "profile_views":
result.ProfileViews = value;
break;
}
}
}
return result;
}
public async Task<InstagramPostPublishResult> PublishImagePostAsync(string igUserId, string imageUrl, string caption)
{
var account = await _accountRepository.GetByInstagramUserIdAsync(igUserId);
if (account == null)
{
throw new Exception("No se encontró la cuenta de Instagram asociada");
}
await EnsureAccessTokenIsValidAsync(account);
// Paso 1: Crear el contenedor de media
var createMediaUrl = $"https://graph.facebook.com/{_facebookGraphVersion}/{igUserId}/media";
var createMediaContent = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"image_url", imageUrl},
{"caption", caption},
{"access_token", account.AccessToken}
});
var mediaResponse = await _httpClient.PostAsync(createMediaUrl, createMediaContent);
mediaResponse.EnsureSuccessStatusCode();
var mediaJson = await mediaResponse.Content.ReadAsStringAsync();
var mediaObj = JsonConvert.DeserializeObject<CreationMediaResponse>(mediaJson);
var creationId = mediaObj.Id;
// Paso 2: Publicar el post
var publishUrl = $"https://graph.facebook.com/{_facebookGraphVersion}/{igUserId}/media_publish";
var publishContent = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"creation_id", creationId},
{"access_token", account.AccessToken}
});
var publishResponse = await _httpClient.PostAsync(publishUrl, publishContent);
publishResponse.EnsureSuccessStatusCode();
var publishJson = await publishResponse.Content.ReadAsStringAsync();
var publishObj = JsonConvert.DeserializeObject<PublishResponse>(publishJson);
return new InstagramPostPublishResult
{
PostId = publishObj.Id
};
}
public async Task RefreshAccessTokenAsync(string accountId)
{
// Método para refrescar el token a largo plazo.
// Deberás tener el long-lived token y el proceso para extenderlo.
// Ver la documentación: https://developers.facebook.com/docs/instagram-api/getting-started
var account = await _accountRepository.GetByIdAsync(Guid.Parse(accountId));
if (account == null) throw new Exception("Cuenta no encontrada.");
// Ejemplo de refreshing (ficticio, debe adaptarse a tu lógica):
var refreshUrl = $"https://graph.facebook.com/{_facebookGraphVersion}/oauth/access_token?" +
$"grant_type=ig_refresh_token&access_token={account.AccessToken}";
var response = await _httpClient.GetAsync(refreshUrl);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var tokenObj = JsonConvert.DeserializeObject<TokenRefreshResponse>(json);
// Actualizar el token en la base de datos
account.UpdateAccessToken(tokenObj.AccessToken, DateTime.UtcNow.AddDays(60)); // Ejemplo de vigencia
await _accountRepository.UpdateAsync(account);
}
private async Task EnsureAccessTokenIsValidAsync(SocialMediaAccount account)
{
// Lógica para validar expiración y refrescar el token si es necesario
if (account.AccessTokenExpiration <= DateTime.UtcNow.AddDays(1))
{
// El token expira pronto, refrescarlo
// Esta lógica puede simplemente llamar a RefreshAccessTokenAsync
// si tenemos almacenado el accountId.
await RefreshAccessTokenAsync(account.Id.ToString());
}
}
}
// Clases internas para deserializar las respuestas JSON
class InstagramInsightsRoot
{
[JsonProperty("data")]
public List<InsightsData> Data { get; set; }
}
class InsightsData
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("values")]
public List<InsightsValue> Values { get; set; }
}
class InsightsValue
{
[JsonProperty("value")]
public int Value { get; set; }
}
class CreationMediaResponse
{
[JsonProperty("id")]
public string Id { get; set; }
}
class PublishResponse
{
[JsonProperty("id")]
public string Id { get; set; }
}
class TokenRefreshResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
// Otros campos, ej: expires_in si aplica
}
Configuración del HttpClient
En el Startup.cs
o el módulo de AspNetBoilerplate, se puede configurar el HttpClient y la inyección de dependencias:
// Startup.cs o equivalente
services.AddHttpClient<IInstagramApiService, InstagramApiAdapter>(client =>
{
// Ajustes del HttpClient
});
Uso en un Servicio de Aplicación
Un ApplicationService (capa de aplicación AspNetBoilerplate) podría consumir IInstagramApiService
así:
public class SocialMediaAppService : ApplicationService
{
private readonly IInstagramApiService _instagramApiService;
public SocialMediaAppService(IInstagramApiService instagramApiService)
{
_instagramApiService = instagramApiService;
}
public async Task<InstagramInsightsDto> GetAccountMetricsAsync(string igUserId)
{
return await _instagramApiService.GetAccountMetricsAsync(igUserId);
}
public async Task<string> PublishPostAsync(string igUserId, string imageUrl, string caption)
{
var result = await _instagramApiService.PublishImagePostAsync(igUserId, imageUrl, caption);
return result.PostId;
}
}
Conclusión
Este ejemplo presenta la estructura general del código para integrarse con la API de Meta (Instagram Graph API), incluyendo:
- Interfaz de servicio de dominio/aplicación (
IInstagramApiService
). - Adaptador (
InstagramApiAdapter
) que encapsula las llamadas a la API externa. - Modelos de datos (DTOs) para deserializar respuestas.
- Ejemplos de cómo actualizar el token y publicar posts.
En un escenario real, se requerirá:
- Añadir manejo de errores más robusto.
- Controlar excepciones por status codes no exitosos.
- Implementar el flujo inicial de obtención de
AccessToken
. - Añadir pruebas unitarias y de integración.
- Integrar con la lógica del dominio DDD existente y repositorios concretos (EF Core u otro).