From 2c5fb3cb6a8e4188127a9444c77e93f157de30f8 Mon Sep 17 00:00:00 2001 From: skyjoshua Date: Fri, 20 Mar 2026 19:49:56 +0000 Subject: [PATCH] hug command --- SkyBot/Commands/Fun/Hug.cs | 123 +++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 SkyBot/Commands/Fun/Hug.cs diff --git a/SkyBot/Commands/Fun/Hug.cs b/SkyBot/Commands/Fun/Hug.cs new file mode 100644 index 0000000..4ea53f0 --- /dev/null +++ b/SkyBot/Commands/Fun/Hug.cs @@ -0,0 +1,123 @@ +using System.Collections.Concurrent; +using System.Text.Json; +using SkyBot.Helpers; +using SkyBot.Models; +using Valour.Sdk.Models; +using Valour.Shared.Models; + +namespace SkyBot.Commands +{ + public class Hug : ICommand + { + public string Name => "hug"; + public string[] Aliases => []; + public string Description => "Send a hug with a random gif."; + public string Section => "Fun"; + public string Usage => "hug [@user]"; + + private static readonly HttpClient _http = new() + { + DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0" } } + }; + + public async Task Execute(CommandContext ctx) + { + ConcurrentDictionary channelCache = ctx.ChannelCache; + long channelId = ctx.ChannelId; + + if (!channelCache.TryGetValue(channelId, out var channel)) return; + + await channel.SendIsTyping(); + + // Fetch a random hug gif from nekos.best + string json; + try + { + json = await _http.GetStringAsync("https://nekos.best/api/v2/hug"); + } + catch + { + await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a hug gif. Try again later."); + return; + } + + using var doc = JsonDocument.Parse(json); + var results = doc.RootElement.GetProperty("results"); + string gifUrl = results[0].GetProperty("url").GetString()!; + + // Download the gif bytes + byte[] gifBytes; + try + { + gifBytes = await _http.GetByteArrayAsync(gifUrl); + } + catch + { + await MessageHelper.ReplyAsync(ctx, channel, "Could not download the hug gif. Try again later."); + return; + } + + // Read GIF dimensions from header (bytes 6-9, little-endian) + int width = 0, height = 0; + if (gifBytes.Length >= 10) + { + width = gifBytes[6] | (gifBytes[7] << 8); + height = gifBytes[8] | (gifBytes[9] << 8); + } + + // Upload to Valour CDN + string cdnUrl; + try + { + using var form = new MultipartFormDataContent(); + using var fileContent = new ByteArrayContent(gifBytes); + fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/gif"); + form.Add(fileContent, "file", "hug.gif"); + + var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse("upload/image", form); + if (!uploadResult.Success) + { + await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the hug gif. Try again later."); + return; + } + cdnUrl = uploadResult.Data!; + } + catch + { + await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the hug gif. Try again later."); + return; + } + + // Resolve the hug target: replied-to message author > args > nobody + string? target = null; + if (ctx.Message.ReplyToId is not null) + { + var replied = await ctx.Message.FetchReplyMessageAsync(); + if (replied is not null) + { + var author = await replied.FetchAuthorAsync(); + if (author is not null) + target = author.Name; + } + } + if (target is null && ctx.Args.Length > 0) + target = string.Join(" ", ctx.Args); + + string sender = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown"; + string text = target is not null + ? $"{sender} hugs {target}! 🤗" + : $"{sender} wants a hug! 🤗"; + + var attachment = new MessageAttachment(MessageAttachmentType.Image) + { + Location = cdnUrl, + MimeType = "image/gif", + FileName = "hug.gif", + Width = width, + Height = height + }; + + await channel.SendMessageAsync(text, attachments: [attachment]); + } + } +}