1+ using Microsoft . AspNetCore . Mvc ;
2+ using RandomAPI . Models ;
3+ using RandomAPI . Repository ;
4+ using static IWebhookService ;
5+
6+ namespace RandomAPI . Services . Webhooks
7+ {
8+ public class WebhookActionService : BaseWebhookService , IWebhookService
9+ {
10+
11+ public WebhookActionService ( IWebhookRepository repo , ILogger < IWebhookService > logger )
12+ : base ( repo , logger ) { }
13+
14+ public async Task < IActionResult > HandleGetListenersActionAsync ( )
15+ {
16+ var urls = await base . GetListenersAsync ( ) ;
17+ return new OkObjectResult ( urls ) ;
18+ }
19+
20+ public async Task < IActionResult > HandleGetListenersOfTypeAsync ( WebhookType type )
21+ {
22+ var urls = await base . GetListenersAsync ( type ) ;
23+ return new OkObjectResult ( urls ) ;
24+ }
25+
26+ public async Task < IActionResult > HandleRegisterActionAsync ( [ FromBody ] string url , IWebhookService . WebhookType type = default )
27+ {
28+ if ( string . IsNullOrWhiteSpace ( url ) )
29+ return new BadRequestObjectResult ( "URL cannot be empty." ) ;
30+ //neede both on regisdter and deregister
31+ url = url . Trim ( ) ;
32+ var safeUrlForLog = url . Replace ( "\r " , "" ) . Replace ( "\n " , "" ) ;
33+
34+ await base . AddListenerAsync ( url , type ) ;
35+
36+ _logger . LogInformation ( "Registered new webhook listener: {Url}" , safeUrlForLog ) ;
37+
38+ return new OkObjectResult ( new { Message = $ "Listener added successfully: { url } " } ) ;
39+ }
40+
41+ public async Task < IActionResult > HandleUnregisterActionAsync ( [ FromBody ] string url )
42+ {
43+ string safeUrlForLog = url ;
44+ if ( string . IsNullOrWhiteSpace ( url ) )
45+ {
46+ safeUrlForLog = url . Replace ( "\r " , "" ) . Replace ( "\n " , "" ) ;
47+ return new BadRequestObjectResult ( "URL cannot be empty." ) ;
48+ }
49+ url = url . Trim ( ) ;
50+
51+ var removed = await base . RemoveListenerAsync ( url ) ;
52+
53+ if ( ! removed )
54+ {
55+ return new NotFoundObjectResult ( new { Message = $ "URL not found: { url } " } ) ;
56+ }
57+
58+ _logger . LogInformation ( "Unregistered webhook listener: {Url}" , safeUrlForLog ) ;
59+ return new OkObjectResult ( new { Message = $ "Listener removed: { url } " } ) ;
60+ }
61+
62+ public async Task < IActionResult > HandleBroadcastActionAsync ( [ FromBody ] IWebHookPayload payload )
63+ {
64+ var listeners = await base . GetListenersAsync ( ) ;
65+
66+ if ( ! listeners . Any ( ) )
67+ return new BadRequestObjectResult ( "No listeners registered to broadcast to." ) ;
68+
69+ switch ( payload )
70+ {
71+ case WebhookPayload p :
72+ p . Timestamp = DateTime . UtcNow ;
73+
74+ break ;
75+
76+ case DiscordWebhookPayload p :
77+ break ;
78+
79+ default :
80+ _logger . LogWarning ( "Received unsupported payload type: {Type}" , payload . GetType ( ) . Name ) ;
81+ return new BadRequestObjectResult ( new { Message = "Unsupported webhook payload type." } ) ;
82+ }
83+
84+ _logger . LogInformation ( "Broadcasting test payload: {Message}" , payload . content ) ;
85+ await base . BroadcastAsync ( payload ) ;
86+ return new OkObjectResult ( new
87+ {
88+ Message = $ "Broadcast sent for message: '{ payload . content } '. Check logs for delivery status."
89+ } ) ;
90+ }
91+ }
92+
93+
94+ public class BaseWebhookService
95+ {
96+ protected readonly IWebhookRepository _repo ;
97+ protected readonly HttpClient _client = new ( ) ;
98+ protected readonly ILogger < IWebhookService > _logger ;
99+
100+ public BaseWebhookService ( IWebhookRepository repo , ILogger < IWebhookService > logger )
101+ {
102+ _repo = repo ;
103+ _logger = logger ;
104+ }
105+
106+ public async Task < IEnumerable < string > > GetListenersAsync ( )
107+ {
108+ var urls = await _repo . GetAllUrlsAsync ( ) ;
109+ return urls . Select ( u => u . Url ) ;
110+ }
111+
112+ public async Task < IEnumerable < string > > GetListenersAsync ( WebhookType type = WebhookType . Default )
113+ {
114+ var urls = await _repo . GetUrlsOfTypeAsync ( type ) ;
115+ return urls . Select ( u => u . Url ) ;
116+ }
117+
118+ public async Task AddListenerAsync ( string url , WebhookType type = default )
119+ {
120+ await _repo . AddUrlAsync ( url , type ) ;
121+ }
122+
123+ public async Task < bool > RemoveListenerAsync ( string url )
124+ {
125+ var result = await _repo . DeleteUrlAsync ( url ) ;
126+ return result > 0 ;
127+ }
128+
129+ //basic broadcast for all
130+ public async Task BroadcastAsync < T > ( T payload ) where T : class
131+ {
132+ IEnumerable < string > urls = await GetListenersAsync ( ) ;
133+ await BroadcastAsync ( payload , urls ) ;
134+ }
135+ //derived for the payloads
136+ public async Task BroadcastAsync < T > ( T payload , IEnumerable < string > urls ) where T : class
137+ {
138+ var tasks = urls . Select ( async url =>
139+ {
140+ try
141+ {
142+ await _client . PostAsJsonAsync ( url , payload ) ;
143+ }
144+ catch ( Exception ex )
145+ {
146+ _logger . LogWarning ( ex , "Webhook POST failed for URL: {url}" , url ) ;
147+ }
148+ } ) ;
149+ await Task . WhenAll ( tasks ) ;
150+ }
151+ }
152+ }
0 commit comments