Helper that posts images or text to Discord webhooks from the Editor. It builds multipart form-data with image bytes and optional message payload, and posts JSON text payloads for simple messages.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Sandbox;
namespace Editor.SuperShot;
public static class DiscordWebhook
{
public static async Task<bool> PostImage( string webhookUrl, byte[] imageBytes, string filename, DiscordMessage message, string mimeType = "image/png" )
{
if ( string.IsNullOrWhiteSpace( webhookUrl ) )
{
Log.Warning( "[Supershot] No Discord webhook URL configured." );
return false;
}
if ( imageBytes is null || imageBytes.Length == 0 )
{
Log.Warning( "[Supershot] Nothing to post to Discord." );
return false;
}
try
{
using var form = new MultipartFormDataContent();
var payload = message is null ? "{}" : BuildPayload( message, filename );
form.Add( new StringContent( payload, Encoding.UTF8, "application/json" ), "payload_json" );
var file = new ByteArrayContent( imageBytes );
file.Headers.ContentType = new MediaTypeHeaderValue( mimeType );
form.Add( file, "files[0]", filename );
await Http.RequestStringAsync( webhookUrl, "POST", form );
Log.Info( $"[Supershot] Posted {filename} to Discord." );
return true;
}
catch ( Exception e )
{
Log.Error( $"[Supershot] Discord post failed: {e.Message}" );
return false;
}
}
public static async Task<int> PostImageToMany( IEnumerable<DiscordWebhookConfig> webhooks, byte[] imageBytes, string filename, DiscordMessage message, string mimeType = "image/png" )
{
int sent = 0;
foreach ( var wh in webhooks )
{
if ( wh is null || !wh.IsConfigured )
continue;
if ( await PostImage( wh.Url, imageBytes, filename, message, mimeType ) )
sent++;
}
return sent;
}
public static async Task<bool> PostText( string webhookUrl, string content )
{
if ( string.IsNullOrWhiteSpace( webhookUrl ) )
{
Log.Warning( "[Supershot] No Discord webhook URL configured." );
return false;
}
if ( string.IsNullOrWhiteSpace( content ) )
content = "Supershot Discord test message.";
try
{
var payload = Json.Serialize( new Dictionary<string, object>
{
["content"] = content
} );
await Http.RequestStringAsync( webhookUrl, "POST", new StringContent( payload, Encoding.UTF8, "application/json" ) );
return true;
}
catch ( Exception e )
{
Log.Error( $"[Supershot] Discord test failed: {e.Message}" );
return false;
}
}
static string BuildPayload( DiscordMessage message, string filename )
{
var payload = new Dictionary<string, object>();
if ( !string.IsNullOrWhiteSpace( message.Content ) )
payload["content"] = message.Content;
if ( !string.IsNullOrWhiteSpace( message.Username ) )
payload["username"] = message.Username;
if ( !string.IsNullOrWhiteSpace( message.AvatarUrl ) )
payload["avatar_url"] = message.AvatarUrl;
return Json.Serialize( payload );
}
}