webhook functions

This commit is contained in:
adam 2025-05-14 23:44:03 -04:00
parent 0c1370bd04
commit bbf94d215f
6 changed files with 207 additions and 5 deletions

View File

@ -24,7 +24,6 @@ public class TwitchSummon : Behavior
OwnerId = uacID,
DisplayName = Name
};
Rememberer.RememberUAC(myUAC);
}
}
public override bool ShouldAct(Message message)

181
Behavior/Webhook.cs Normal file
View File

@ -0,0 +1,181 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using vassago.Models;
[StaticPlz]
public class Webhook : Behavior
{
public override string Name => "Webhook";
public override string Trigger => "!hook";
public override string Description => "call a webhook";
private static List<WebhookConf> configuredWebhooks = new List<WebhookConf>();
private ConcurrentDictionary<Guid, WebhookActionOrder> authedCache = new ConcurrentDictionary<Guid, WebhookActionOrder>();
private HttpClient hc = new HttpClient();
public static void SetupWebhooks(IConfigurationSection confSection)
{
configuredWebhooks = confSection.Get<List<vassago.Behavior.WebhookConf>>();
foreach (var conf in configuredWebhooks)
{
var confName = $"Webhook: {conf.Trigger}";
Console.WriteLine($"confName: {confName}; conf.uri: {conf.Uri}, conf.uacID: {conf.uacID}, conf.Method: {conf.Method}, conf.Headers: {conf.Headers?.Count() ?? 0}, conf.Content: {conf.Content}");
foreach (var kvp in conf.Headers)
{
Console.WriteLine($"{kvp[0]}: {kvp[1]}");
}
var myUAC = Rememberer.SearchUAC(uac => uac.OwnerId == conf.uacID);
if (myUAC == null)
{
myUAC = new()
{
OwnerId = conf.uacID,
DisplayName = confName
};
Rememberer.RememberUAC(myUAC);
}
else
{
if (myUAC.DisplayName != confName)
{
myUAC.DisplayName = confName;
Rememberer.RememberUAC(myUAC);
}
}
}
}
public override bool ShouldAct(Message message)
{
if (!base.ShouldAct(message))
return false;
Console.WriteLine("webhook checking");
if (configuredWebhooks?.Count() < 1)
{
Console.Error.WriteLine("no webhooks configured!");
}
var webhookableMessageContent = message.Content.Substring(message.Content.IndexOf(Trigger) + Trigger.Length + 1);
Console.WriteLine($"webhookable content: {webhookableMessageContent}");
foreach (var wh in configuredWebhooks)
{
if (webhookableMessageContent.StartsWith(wh.Trigger))
{
var uacConf = Rememberer.SearchUAC(uac => uac.OwnerId == wh.uacID);
if (uacConf.Users.Contains(message.Author.IsUser) || uacConf.Channels.Contains(message.Channel) || uacConf.AccountInChannels.Contains(message.Author))
{
Console.WriteLine("webhook UAC passed, preparing WebhookActionOrder");
authedCache.TryAdd(message.Id, new WebhookActionOrder()
{
Conf = wh,
webhookContent = webhookableMessageContent.Substring(wh.Trigger.Length + 1),
});
Console.WriteLine($"added {message.Id} to authedcache");
return true;
}
}
}
return false;
}
public override async Task<bool> ActOn(Message message)
{
Console.WriteLine($"hi i'm ActOn. acting on {message.Id}");
WebhookActionOrder actionOrder;
if (!authedCache.TryRemove(message.Id, out actionOrder))
{
Console.Error.WriteLine($"{message.Id} was supposed to act, but authedCache doesn't have it! it has {authedCache?.Count()} other stuff, though.");
return false;
}
var req = new HttpRequestMessage(new HttpMethod(actionOrder.Conf.Method.ToString()), actionOrder.Conf.Uri);
var theContentHeader = actionOrder.Conf.Headers?.FirstOrDefault(h => h[0]?.ToLower() == "content-type");
if (theContentHeader != null)
{
switch (theContentHeader[1]?.ToLower())
{
//json content is constructed some other weird way.
case "multipart/form-data":
req.Content = new System.Net.Http.MultipartFormDataContent(translate(actionOrder));
break;
default:
req.Content = new System.Net.Http.StringContent(translate(actionOrder));
break;
}
req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(theContentHeader[1]?.ToLower());
}
if (req.Content == null)
{
req.Content = new System.Net.Http.StringContent(translate(actionOrder));
}
Console.WriteLine($"survived translating string content. request content: {req.Content}");
if (actionOrder.Conf.Headers?.ToList().Count > 0)
{
Console.WriteLine("will add headers.");
foreach (var kvp in actionOrder.Conf.Headers.ToList())
{
if (kvp[0] == theContentHeader[0])
{
Console.WriteLine("content header; skipping.");
}
else
{
Console.WriteLine($"adding header; {kvp[0]}: {kvp[1]}");
req.Headers.Add(kvp[0], kvp[1]);
Console.WriteLine("survived.");
}
}
}
else
{
Console.WriteLine("no headers to add.");
}
Console.WriteLine("about to Send.");
var response = hc.Send(req);
Console.WriteLine($"{response.StatusCode} - {response.ReasonPhrase}");
if (!response.IsSuccessStatusCode)
{
var tragedy = $"{response.StatusCode} - {response.ReasonPhrase} - {await response.Content.ReadAsStringAsync()}";
Console.Error.WriteLine(tragedy);
await message.Reply(tragedy);
}
else
{
await message.Reply(await response.Content.ReadAsStringAsync());
}
return true;
}
private string translate(WebhookActionOrder actionOrder)
{
//TODO: message author, etc.
return actionOrder.Conf.Content?.Replace("{text}", actionOrder.webhookContent);
}
}
public class WebhookConf
{
public Guid uacID { get; set; }
public string Trigger { get; set; }
public Uri Uri { get; set; }
//public HttpMethod Method { get; set; }
public Enumerations.HttpVerb Method { get; set; }
public List<List<string>> Headers { get; set; }
public string Content { get; set; }
}
public class WebhookActionOrder
{
public WebhookConf Conf { get; set; }
public string webhookContent { get; set; }
}

View File

@ -16,6 +16,7 @@ namespace vassago
DiscordTokens = aspConfig.GetSection("DiscordTokens").Get<IEnumerable<string>>();
TwitchConfigs = aspConfig.GetSection("TwitchConfigs").Get<IEnumerable<TwitchConfig>>();
Conversion.Converter.Load(aspConfig["ExchangePairsLocation"]);
vassago.Behavior.Webhook.SetupWebhooks(aspConfig.GetSection("Webhooks"));
}
IEnumerable<string> DiscordTokens { get; }
@ -51,4 +52,4 @@ namespace vassago
return null;
}
}
}
}

View File

@ -38,6 +38,20 @@ public static class Enumerations
[Description("organizational psuedo-channel")]
OU
}
///<summary>
///bro. don't even get me started. tl;dr: hashtag microsoft.
///</summary>
public enum HttpVerb
{
Get,
Post,
Put,
Delete,
Head,
Patch,
Options
}
public static string GetDescription<T>(this T enumerationValue)
where T : struct
@ -64,4 +78,4 @@ public static class Enumerations
//If we have no description attribute, just return the ToString of the enum
return enumerationValue.ToString();
}
}
}

View File

@ -45,7 +45,7 @@ app.UseSwaggerUI(c =>
c.SwaggerEndpoint("/swagger/v1/swagger.json", "api");
});
app.UseExceptionHandler();
//app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())

View File

@ -14,5 +14,12 @@
],
"exchangePairsLocation": "assets/exchangepairs.json",
"DBConnectionString": "Host=azure.club;Database=db;Username=user;Password=password",
"SetupSlashCommands": false
"SetupSlashCommands": false,
"Webhooks": [
{
"uacID": "9a94855a-e5a2-43b5-8420-ce670472ce95",
"Trigger": "test",
"Uri": "http://localhost"
}
]
}