using System.Security.Cryptography; using System.Text; using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using MktXService.Infrastructure.ExternalServices.Twitter; namespace MktXService.API.Controllers; /// /// EN: Controller for Twitter Webhook handling. /// VI: Controller xử lý Twitter Webhook. /// [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/webhooks/twitter")] [Produces("application/json")] [Authorize] public class WebhooksController : ControllerBase { private readonly ITwitterApiClient _twitterClient; private readonly TwitterApiOptions _options; private readonly ILogger _logger; public WebhooksController( ITwitterApiClient twitterClient, IOptions options, ILogger logger) { _twitterClient = twitterClient; _options = options.Value; _logger = logger; } /// /// EN: CRC token verification for Twitter webhook. /// VI: Xác thực CRC token cho Twitter webhook. /// [HttpGet] [AllowAnonymous] [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult VerifyCrcToken([FromQuery(Name = "crc_token")] string crcToken) { _logger.LogInformation("Received CRC challenge: {Token}", crcToken); var response = _twitterClient.GenerateCrcResponse(crcToken); return Ok(new { response_token = response }); } /// /// EN: Receive Twitter webhook events. /// VI: Nhận events từ Twitter webhook. /// [HttpPost] [AllowAnonymous] [ProducesResponseType(StatusCodes.Status200OK)] public async Task ReceiveWebhookEvent([FromBody] TwitterWebhookPayload payload) { _logger.LogInformation("Received webhook event for user: {UserId}", payload.ForUserId); // EN: Process Direct Message events // VI: Xử lý events Direct Message if (payload.DirectMessageEvents?.Any() == true) { foreach (var dmEvent in payload.DirectMessageEvents) { _logger.LogInformation("Received DM from {SenderId}: {MessageId}", dmEvent.MessageCreate?.SenderId, dmEvent.Id); // EN: In production, queue for async processing // VI: Trong production, đưa vào hàng đợi để xử lý bất đồng bộ } } // EN: Process follow events // VI: Xử lý events follow if (payload.FollowEvents?.Any() == true) { foreach (var followEvent in payload.FollowEvents) { _logger.LogInformation("New follower: {FollowerId}", followEvent.Source?.Id); } } return Ok(); } /// /// EN: Register webhook for an account. /// VI: Đăng ký webhook cho tài khoản. /// [HttpPost("register")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task RegisterWebhook([FromBody] RegisterWebhookRequest request) { try { var webhookId = await _twitterClient.RegisterWebhookAsync(request.WebhookUrl); return Ok(new { success = true, webhookId }); } catch (Exception ex) { _logger.LogError(ex, "Failed to register webhook"); return BadRequest(new { success = false, error = ex.Message }); } } /// /// EN: Subscribe to webhook events. /// VI: Đăng ký nhận webhook events. /// [HttpPost("subscribe")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task SubscribeToWebhook() { try { await _twitterClient.SubscribeToWebhookAsync(); return Ok(new { success = true, message = "Subscribed to webhook events" }); } catch (Exception ex) { _logger.LogError(ex, "Failed to subscribe to webhook"); return BadRequest(new { success = false, error = ex.Message }); } } } #region Webhook DTOs public class TwitterWebhookPayload { public string? ForUserId { get; set; } public List? DirectMessageEvents { get; set; } public List? FollowEvents { get; set; } public Dictionary? Users { get; set; } } public class DirectMessageEvent { public string? Id { get; set; } public string? Type { get; set; } public string? CreatedTimestamp { get; set; } public MessageCreate? MessageCreate { get; set; } } public class MessageCreate { public string? SenderId { get; set; } public string? Target { get; set; } public MessageData? MessageData { get; set; } } public class MessageData { public string? Text { get; set; } public List? Entities { get; set; } } public class FollowEvent { public string? Type { get; set; } public string? CreatedTimestamp { get; set; } public TwitterUserData? Source { get; set; } public TwitterUserData? Target { get; set; } } public class TwitterUserData { public string? Id { get; set; } public string? Name { get; set; } public string? ScreenName { get; set; } } public record RegisterWebhookRequest(string WebhookUrl); #endregion