Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
0c50e78cf4
|
|||
| ebbdc17b15 | |||
| d5e51d0cba | |||
| 9cd0a6df78 | |||
| b787a9e3c6 | |||
| 4f6ae37eca | |||
| 2cf3798728 | |||
| 88b647b134 | |||
| cebba3948a | |||
| 8a1f295591 | |||
| 9e4a3ffb16 | |||
| 5b1394ad6a | |||
| 06c088c4f8 | |||
|
|
5e3da35bd1 | ||
|
|
c330c3f2e3 | ||
|
|
aa8b3bbb8c | ||
|
|
ff9fbc67f2 | ||
|
|
7059b5dce5 | ||
|
|
5e60a4b7a3 | ||
|
|
5b18a2170b | ||
|
|
6d546d81c0 | ||
|
|
f8085d508b | ||
|
|
5e91b3063b | ||
|
|
3cc66250e5 | ||
|
|
ad87e93e6a | ||
|
|
ddf4e3cc87 | ||
|
|
b43cfa4b17 | ||
|
|
72dfaf7434 | ||
|
|
3631f71ab5 | ||
| ea4aa56f5d | |||
| 0fb95eedef | |||
| 418d2ba00e | |||
| 44448acde6 | |||
| bf27843e82 |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
.env
|
||||
.gitignore
|
||||
SkyBot/bin/
|
||||
SkyBot/obj/
|
||||
SkyBot/SkyBot.sln
|
||||
SkyBot/database.db
|
||||
SkyBot/Config.cs
|
||||
**/bin/
|
||||
**/obj/
|
||||
**/SkyBot*.sln
|
||||
**/database.db
|
||||
**/Config.cs
|
||||
|
||||
114
COMMANDS.md
Normal file
114
COMMANDS.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# SkyBot Commands
|
||||
|
||||
All commands are prefixed with your configured prefix (e.g. `s/`). Arguments in `[brackets]` are optional.
|
||||
|
||||
---
|
||||
|
||||
## Fun
|
||||
|
||||
| Command | Aliases | Description |
|
||||
|---|---|---|
|
||||
| `8ball [question]` | — | Ask the magic 8 ball a question |
|
||||
| `cat` | — | Post a random cat picture |
|
||||
| `choose <options>` | — | Pick one of the given options |
|
||||
| `coinflip` | — | Flip a coin |
|
||||
| `dice` | — | Roll a die |
|
||||
| `echo <text>` | — | Repeat text through the bot |
|
||||
| `hangman [category]` | — | Start a channel-wide game of hangman (`hg <letter or word>` to guess) |
|
||||
| `image <query>` | `img` | Fetch a random image matching your search |
|
||||
| `mock` | — | mOcK tExT of a message (reply to one) |
|
||||
| `reverse` | — | Reverse a message (reply to one or pass text) |
|
||||
| `rockpaperscissors` | `rps` | Play rock paper scissors against the bot |
|
||||
| `t9encode <text>` | — | Encode text into old phone keypad multi-tap digits |
|
||||
| `t9decode <digits>` | — | Decode old phone keypad multi-tap digits |
|
||||
| `trivia` | — | Start a channel-wide trivia question with 30 seconds to answer (`tg <A/B/C/D>` to guess) |
|
||||
| `wordle` | — | Start a channel-wide Wordle; guess the 5-letter word in 6 tries (`wg <word>` to guess) |
|
||||
|
||||
---
|
||||
|
||||
## Info
|
||||
|
||||
| Command | Aliases | Description |
|
||||
|---|---|---|
|
||||
| `botguide` | `bot`, `bguide` | Link to the Valour bot guide |
|
||||
| `devcentral` | — | Invite link to the Dev Central planet |
|
||||
| `info` | — | User and planet info |
|
||||
| `joinsite` | — | Link to a site to help bots join a planet |
|
||||
| `minecraft` | — | Unofficial ValourSMP server IPs |
|
||||
| `ping` | — | Check bot latency |
|
||||
| `source` | — | Link to the bot's source code |
|
||||
| `suggest` | — | Sends a link to where you can make suggestions |
|
||||
| `swagger` | — | Link to the Valour API docs |
|
||||
| `uptime` | — | Show how long the bot has been running |
|
||||
| `usercount` | — | Show the total Valour user count |
|
||||
| `version` | — | Show the current bot and Valour SDK version |
|
||||
|
||||
---
|
||||
|
||||
## Moderation
|
||||
|
||||
| Command | Aliases | Description |
|
||||
|---|---|---|
|
||||
| `ban <@user> [reason]` | — | Ban a member from the planet |
|
||||
| `bans` | — | List all bans in the planet |
|
||||
| `kick <@user> [reason]` | — | Kick a member from the planet |
|
||||
| `setwelcome` | — | Configure a welcome channel and message |
|
||||
| `unban <user id>` | — | Unban a member from the planet |
|
||||
|
||||
---
|
||||
|
||||
## RP
|
||||
|
||||
All RP commands accept an optional `[@user]` argument or a message reply to target someone.
|
||||
|
||||
### Marriage
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `marriage propose @user` | Propose to someone (reply to their message or mention them). Proposal expires after 5 minutes. |
|
||||
| `marriage accept` | Accept a pending proposal |
|
||||
| `marriage decline` | Decline a pending proposal |
|
||||
| `marriage status` | Show your current marriage status |
|
||||
| `marriage divorce` | End your current marriage |
|
||||
|
||||
Marriages persist across bot restarts.
|
||||
|
||||
### GIF Commands
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `angry` | Express anger |
|
||||
| `baka` | Call someone a baka |
|
||||
| `bite` | Bite someone |
|
||||
| `blowkiss` | Blow a kiss |
|
||||
| `blush` | Blush |
|
||||
| `bonk` | Bonk someone |
|
||||
| `carry` | Carry someone |
|
||||
| `clap` | Clap |
|
||||
| `cry` | Cry |
|
||||
| `cuddle` | Cuddle someone |
|
||||
| `dance` | Dance |
|
||||
| `facepalm` | Facepalm |
|
||||
| `happy` | Express happiness |
|
||||
| `holdhand` | Hold hands with someone |
|
||||
| `hug` | Hug someone |
|
||||
| `kiss` | Kiss someone |
|
||||
| `laugh` | Laugh |
|
||||
| `lurk` | Lurk |
|
||||
| `nom` | Nom someone |
|
||||
| `nya` | Nya |
|
||||
| `pat` | Pat someone |
|
||||
| `poke` | Poke someone |
|
||||
| `pout` | Pout |
|
||||
| `punch` | Punch someone |
|
||||
| `run` | Run |
|
||||
| `shocked` | Act shocked |
|
||||
| `sleep` | Sleep |
|
||||
| `smug` | Be smug |
|
||||
| `spin` | Spin |
|
||||
| `tableflip` | Flip a table |
|
||||
| `teehee` | Teehee |
|
||||
| `tickle` | Tickle someone |
|
||||
| `wave` | Wave |
|
||||
| `wink` | Wink |
|
||||
| `yawn` | Yawn |
|
||||
@@ -60,7 +60,7 @@ Some features make outbound requests to third-party APIs to fetch content. These
|
||||
- **Datamuse** (datamuse.com) — word lists for Hangman and Wordle
|
||||
- **Open Trivia Database** (opentdb.com) — trivia questions for Trivia
|
||||
- **The Cat API** (thecatapi.com) — random cat images for the cat command
|
||||
- **nekos.best** (nekos.best) — hug GIFs for the hug command
|
||||
- **nekos.best** (nekos.best) — GIFs for all RP commands (hug, pat, kiss, dance, etc.)
|
||||
- **Pixabay** (pixabay.com) — images for the image command
|
||||
|
||||
---
|
||||
|
||||
46
README.md
46
README.md
@@ -11,46 +11,14 @@ SkyBot is a Valour.gg bot built with .NET 10.
|
||||
- Built with .NET 10
|
||||
- Command system with automatic registration
|
||||
|
||||
### Fun
|
||||
### Command Categories
|
||||
|
||||
- `8ball` — ask the magic 8 ball a question
|
||||
- `coinflip` — flip a coin
|
||||
- `dice` — roll a die
|
||||
- `rockpaperscissors` — play rock paper scissors against the bot
|
||||
- `choose` — pick one of the given options
|
||||
- `echo` — repeat text through the bot
|
||||
- `reverse` — reverse yours or a replied message
|
||||
- `mock` — mOcK tExT
|
||||
- `t9encode` / `t9decode` — encode or decode old phone keypad multi-tap digits
|
||||
- `hangman` — channel-wide game of hangman with optional category (`hg <letter or word>` to guess)
|
||||
- `wordle` — channel-wide Wordle; guess the 5-letter word in 6 tries (`wg <word>` to guess)
|
||||
- `trivia` — channel-wide trivia question with 30 seconds to answer (`tg <A/B/C/D>` to guess)
|
||||
- `image` — fetch a random image matching your search (aliases: `img`)
|
||||
- **Fun** — games, utilities, and silly stuff (8ball, hangman, wordle, trivia, and more)
|
||||
- **Info** — bot and platform info commands
|
||||
- **Moderation** — ban, kick, welcome messages
|
||||
- **RP** — roleplay GIF commands powered by nekos.best (35+ commands)
|
||||
|
||||
### Chill
|
||||
|
||||
- `cat` — post a random cat picture
|
||||
- `hug` — send a hug with a random gif
|
||||
|
||||
### Info
|
||||
|
||||
- `ping` — check bot latency
|
||||
- `uptime` — show how long the bot has been running
|
||||
- `info` — user and planet info
|
||||
- `version` — show the current bot and Valour SDK version
|
||||
- `usercount` — show the total Valour user count
|
||||
- `source` — link to the bot's source code
|
||||
- `joinsite` — link to a site to help bots join a planet
|
||||
- `devcentral` — invite link to the Dev Central planet
|
||||
- `swagger` — link to the Valour API docs
|
||||
- `minecraft` — Unofficial ValourSMP server IPs
|
||||
- `suggest` — submit a suggestion for the bot
|
||||
|
||||
### Moderation
|
||||
|
||||
- `ban` / `unban` / `kick` — member moderation
|
||||
- `bans` — list all bans in the planet
|
||||
- `setwelcome` — configure a welcome channel and message
|
||||
Full command list: [COMMANDS.md](COMMANDS.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -126,4 +94,4 @@ Never commit your `.env` file to the repository. Ensure it is listed in your `.g
|
||||
## Running the Bot
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
```
|
||||
|
||||
68
SkyBot/Commands/Dev/Channel.cs
Normal file
68
SkyBot/Commands/Dev/Channel.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Collections.Concurrent;
|
||||
using SkyBot.Helpers;
|
||||
using SkyBot.Models;
|
||||
using SkyBot.Services;
|
||||
using SkyBot.Services.Messages;
|
||||
using Valour.Sdk.Models;
|
||||
using Valour.Shared.Authorization;
|
||||
|
||||
namespace SkyBot.Commands
|
||||
{
|
||||
public class ChannelCmds : ICommand
|
||||
{
|
||||
public string Name => "channel";
|
||||
public string[] Aliases => ["ch"];
|
||||
public string Description => "Enable or disable bot commands in a channel";
|
||||
public string Section => "Dev";
|
||||
public string Usage => "channel <enable|disable>";
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
PlanetMember member = ctx.Member;
|
||||
string[] args = ctx.Args;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
if (!PermissionHelper.IsOwner(member) && !await PermissionHelper.HasPermAsync(member, channel, [ChatChannelPermissions.ManageMessages]))
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "This is a Dev only command.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Usage: `channel <enable|disable>`");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0].ToLower())
|
||||
{
|
||||
case "disable":
|
||||
if (Create.disabledChannels.Contains(channelId))
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "This channel is already disabled.");
|
||||
return;
|
||||
}
|
||||
Create.disabledChannels.Add(channelId);
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Bot commands disabled in this channel.");
|
||||
break;
|
||||
|
||||
case "enable":
|
||||
if (!Create.disabledChannels.Contains(channelId))
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "This channel is not disabled.");
|
||||
return;
|
||||
}
|
||||
Create.disabledChannels.Remove(channelId);
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Bot commands enabled in this channel.");
|
||||
break;
|
||||
|
||||
default:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Usage: `channel <enable|disable>`");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace SkyBot.Commands
|
||||
public string Name => "cat";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Posts a random cat picture.";
|
||||
public string Section => "Chill";
|
||||
public string Section => "Fun";
|
||||
public string Usage => "cat";
|
||||
|
||||
private static readonly HttpClient _http = new();
|
||||
@@ -95,7 +95,7 @@ namespace SkyBot.Commands
|
||||
Height = height
|
||||
};
|
||||
|
||||
await channel.SendMessageAsync("",attachments: [attachment]);
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "", [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
|
||||
using SkyBot.Helpers;
|
||||
using SkyBot.Models;
|
||||
using Valour.Sdk.Models;
|
||||
using Valour.Shared.Authorization;
|
||||
|
||||
namespace SkyBot.Commands
|
||||
{
|
||||
|
||||
@@ -3,10 +3,11 @@ using System.Text;
|
||||
using SkyBot.Helpers;
|
||||
using SkyBot.Models;
|
||||
using Valour.Sdk.Models;
|
||||
using Valour.Shared.Models;
|
||||
|
||||
namespace SkyBot.Commands
|
||||
{
|
||||
public class Info : ICommand
|
||||
public partial class Info : ICommand
|
||||
{
|
||||
public string Name => "info";
|
||||
public string[] Aliases => [];
|
||||
@@ -44,6 +45,21 @@ namespace SkyBot.Commands
|
||||
}
|
||||
}
|
||||
|
||||
[System.Text.RegularExpressions.GeneratedRegex(@"«@m-(\d+)»")]
|
||||
private static partial System.Text.RegularExpressions.Regex MemberMentionRegex();
|
||||
|
||||
private static long? ExtractMemberMentionId(Message message)
|
||||
{
|
||||
var mention = message.Mentions?.FirstOrDefault(m => m.Type == MentionType.PlanetMember);
|
||||
if (mention != null) return mention.TargetId;
|
||||
|
||||
var match = MemberMentionRegex().Match(message.Content ?? "");
|
||||
if (match.Success && long.TryParse(match.Groups[1].Value, out long id))
|
||||
return id;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task HandleUserInfo(CommandContext ctx, Channel channel)
|
||||
{
|
||||
Message message = ctx.Message;
|
||||
@@ -51,24 +67,18 @@ namespace SkyBot.Commands
|
||||
string[] args = ctx.Args;
|
||||
PlanetMember? target;
|
||||
|
||||
try
|
||||
var mentionId = ExtractMemberMentionId(message);
|
||||
|
||||
if (mentionId.HasValue)
|
||||
{
|
||||
if (message.Mentions != null && message.Mentions.Any())
|
||||
{
|
||||
target = await planet.FetchMemberAsync(message.Mentions.First().TargetId);
|
||||
}
|
||||
else if (args.Length > 1 && long.TryParse(args[1], out long memberid))
|
||||
{
|
||||
target = await planet.FetchMemberAsync(memberid);
|
||||
}
|
||||
else
|
||||
{
|
||||
target = ctx.Member;
|
||||
}
|
||||
target = await planet.FetchMemberAsync(mentionId.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
else if (args.Length > 1 && long.TryParse(args[1], out long memberid))
|
||||
{
|
||||
target = await planet.FetchMemberAsync(memberid);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Exception: {ex.Message}");
|
||||
target = ctx.Member;
|
||||
}
|
||||
|
||||
@@ -87,6 +97,7 @@ namespace SkyBot.Commands
|
||||
sb.AppendLine($"Status: `{(string.IsNullOrWhiteSpace(target.Status) ? "None" : target.Status)}`");
|
||||
sb.AppendLine($"Primary Role: `{target.PrimaryRole?.Name ?? "None"}`");
|
||||
sb.AppendLine($"Roles: `{string.Join(", ", target.Roles.Select(r => r.Name))}`");
|
||||
sb.AppendLine($"Valour Join Date: `{target.User.TimeJoined} UTC`");
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, sb.ToString());
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace SkyBot.Commands
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Sends a link to where you can suggest commands.";
|
||||
public string Section => "Info";
|
||||
public string Usage => "source";
|
||||
public string Usage => "suggest";
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
|
||||
118
SkyBot/Commands/RP/Angry.cs
Normal file
118
SkyBot/Commands/RP/Angry.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Angry : ICommand
|
||||
{
|
||||
public string Name => "angry";
|
||||
public string[] Aliases => Array.Empty<string>();
|
||||
public string Description => "Express anger with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "angry [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/angry");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch an angry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the angry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "angry.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the angry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the angry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} is angry at {target}! 😠"
|
||||
: $"{sender} is angry! 😠";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "angry.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Baka.cs
Normal file
118
SkyBot/Commands/RP/Baka.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Baka : ICommand
|
||||
{
|
||||
public string Name => "baka";
|
||||
public string[] Aliases => ["idiot"];
|
||||
public string Description => "Call someone a baka with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "baka [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/baka");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a baka gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the baka gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "baka.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the baka gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the baka gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} calls {target} a baka!"
|
||||
: $"{sender} says baka!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "baka.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Bite.cs
Normal file
118
SkyBot/Commands/RP/Bite.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Bite : ICommand
|
||||
{
|
||||
public string Name => "bite";
|
||||
public string[] Aliases => ["bites"];
|
||||
public string Description => "Bite someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "bite [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/bite");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a bite gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the bite gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "bite.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the bite gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the bite gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} bites {target}!"
|
||||
: $"{sender} wants to bite someone!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "bite.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Blowkiss.cs
Normal file
118
SkyBot/Commands/RP/Blowkiss.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Blowkiss : ICommand
|
||||
{
|
||||
public string Name => "blowkiss";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Blow a kiss with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "blowkiss [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/blowkiss");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a blowkiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the blowkiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "blowkiss.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the blowkiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the blowkiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} blows a kiss to {target}!"
|
||||
: $"{sender} blows a kiss!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "blowkiss.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Blush.cs
Normal file
118
SkyBot/Commands/RP/Blush.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Blush : ICommand
|
||||
{
|
||||
public string Name => "blush";
|
||||
public string[] Aliases => Array.Empty<string>();
|
||||
public string Description => "Blush with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "blush [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/blush");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a blush gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the blush gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "blush.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the blush gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the blush gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} is blushing at {target}!"
|
||||
: $"{sender} is blushing!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "blush.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Bonk.cs
Normal file
118
SkyBot/Commands/RP/Bonk.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Bonk : ICommand
|
||||
{
|
||||
public string Name => "bonk";
|
||||
public string[] Aliases => ["bonks"];
|
||||
public string Description => "Bonk someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "bonk [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/bonk");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a bonk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the bonk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "bonk.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the bonk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the bonk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} bonks {target}!"
|
||||
: $"{sender} is bonking!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "bonk.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Carry.cs
Normal file
118
SkyBot/Commands/RP/Carry.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Carry : ICommand
|
||||
{
|
||||
public string Name => "carry";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Carry someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "carry [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/carry");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a carry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the carry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "carry.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the carry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the carry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} carries {target}!"
|
||||
: $"{sender} wants to carry someone!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "carry.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Clap.cs
Normal file
118
SkyBot/Commands/RP/Clap.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Clap : ICommand
|
||||
{
|
||||
public string Name => "clap";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Clap with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "clap [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/clap");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a clap gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the clap gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "clap.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the clap gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the clap gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} claps for {target}!"
|
||||
: $"{sender} is clapping!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "clap.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Cry.cs
Normal file
118
SkyBot/Commands/RP/Cry.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Cry : ICommand
|
||||
{
|
||||
public string Name => "cry";
|
||||
public string[] Aliases => ["cries"];
|
||||
public string Description => "Cry with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "cry [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/cry");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a cry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the cry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "cry.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the cry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the cry gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} is crying because of {target}!"
|
||||
: $"{sender} is crying!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "cry.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Cuddle.cs
Normal file
118
SkyBot/Commands/RP/Cuddle.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Cuddle : ICommand
|
||||
{
|
||||
public string Name => "cuddle";
|
||||
public string[] Aliases => ["cuddles"];
|
||||
public string Description => "Cuddle someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "cuddle [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/cuddle");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a cuddle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the cuddle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "cuddle.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the cuddle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the cuddle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} cuddles with {target}!"
|
||||
: $"{sender} wants cuddles!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "cuddle.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Dance.cs
Normal file
118
SkyBot/Commands/RP/Dance.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Dance : ICommand
|
||||
{
|
||||
public string Name => "dance";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Dance with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "dance [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/dance");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a dance gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the dance gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "dance.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the dance gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the dance gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} dances with {target}!"
|
||||
: $"{sender} is dancing!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "dance.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Facepalm.cs
Normal file
118
SkyBot/Commands/RP/Facepalm.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Facepalm : ICommand
|
||||
{
|
||||
public string Name => "facepalm";
|
||||
public string[] Aliases => Array.Empty<string>();
|
||||
public string Description => "Facepalm with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "facepalm [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/facepalm");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a facepalm gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the facepalm gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "facepalm.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the facepalm gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the facepalm gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} facepalms at {target}!"
|
||||
: $"{sender} facepalms!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "facepalm.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Happy.cs
Normal file
118
SkyBot/Commands/RP/Happy.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Happy : ICommand
|
||||
{
|
||||
public string Name => "happy";
|
||||
public string[] Aliases => Array.Empty<string>();
|
||||
public string Description => "Express happiness with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "happy [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/happy");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a happy gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the happy gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "happy.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the happy gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the happy gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} is happy with {target}!"
|
||||
: $"{sender} is happy!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "happy.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Holdhand.cs
Normal file
118
SkyBot/Commands/RP/Holdhand.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Holdhand : ICommand
|
||||
{
|
||||
public string Name => "holdhand";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Hold hands with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "holdhand [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/handhold");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a handhold gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the handhold gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "handhold.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the handhold gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the handhold gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} holds hands with {target}!"
|
||||
: $"{sender} wants to hold hands!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "handhold.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,14 @@ namespace SkyBot.Commands
|
||||
public class Hug : ICommand
|
||||
{
|
||||
public string Name => "hug";
|
||||
public string[] Aliases => [];
|
||||
public string[] Aliases => ["hugs"];
|
||||
public string Description => "Send a hug with a random gif.";
|
||||
public string Section => "Chill";
|
||||
public string Section => "RP";
|
||||
public string Usage => "hug [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0" } }
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
@@ -105,8 +105,8 @@ namespace SkyBot.Commands
|
||||
|
||||
string sender = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
string text = target is not null
|
||||
? $"{sender} hugs {target}! 🤗"
|
||||
: $"{sender} wants a hug! 🤗";
|
||||
? $"{sender} hugs {target}!"
|
||||
: $"{sender} wants a hug!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
@@ -117,7 +117,7 @@ namespace SkyBot.Commands
|
||||
Height = height
|
||||
};
|
||||
|
||||
await channel.SendMessageAsync(text, attachments: [attachment]);
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Kiss.cs
Normal file
118
SkyBot/Commands/RP/Kiss.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Kiss : ICommand
|
||||
{
|
||||
public string Name => "kiss";
|
||||
public string[] Aliases => ["kisses"];
|
||||
public string Description => "Kiss someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "kiss [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/kiss");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a kiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the kiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "kiss.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the kiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the kiss gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} kisses {target}!"
|
||||
: $"{sender} wants a kiss!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "kiss.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Laugh.cs
Normal file
118
SkyBot/Commands/RP/Laugh.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Laugh : ICommand
|
||||
{
|
||||
public string Name => "laugh";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Laugh with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "laugh [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/laugh");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a laugh gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the laugh gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "laugh.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the laugh gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the laugh gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} laughs at {target}!"
|
||||
: $"{sender} is laughing!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "laugh.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Lurk.cs
Normal file
118
SkyBot/Commands/RP/Lurk.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Lurk : ICommand
|
||||
{
|
||||
public string Name => "lurk";
|
||||
public string[] Aliases => ["lurks"];
|
||||
public string Description => "Lurk with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "lurk [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/lurk");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a lurk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the lurk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "lurk.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the lurk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the lurk gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} lurks around {target}!"
|
||||
: $"{sender} is lurking!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "lurk.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
348
SkyBot/Commands/RP/Marriage.cs
Normal file
348
SkyBot/Commands/RP/Marriage.cs
Normal file
@@ -0,0 +1,348 @@
|
||||
using System.Collections.Concurrent;
|
||||
using SkyBot.Helpers;
|
||||
using SkyBot.Models;
|
||||
using SkyBot.Services;
|
||||
using Valour.Sdk.Models;
|
||||
|
||||
namespace SkyBot.Commands
|
||||
{
|
||||
public class Marriage : ICommand
|
||||
{
|
||||
public string Name => "marriage";
|
||||
public string[] Aliases => ["marry"];
|
||||
public string Description => "Marriage system — propose, accept, decline, check status, or divorce.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "marriage <propose|accept|decline|status|divorce>";
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
string sub = ctx.Args.Length > 0 ? ctx.Args[0].ToLower() : "status";
|
||||
|
||||
switch (sub)
|
||||
{
|
||||
case "propose":
|
||||
await HandlePropose(ctx, channel);
|
||||
break;
|
||||
case "accept":
|
||||
await HandleAccept(ctx, channel);
|
||||
break;
|
||||
case "decline":
|
||||
await HandleDecline(ctx, channel);
|
||||
break;
|
||||
case "divorce":
|
||||
await HandleDivorce(ctx, channel);
|
||||
break;
|
||||
case "force":
|
||||
await HandleForce(ctx, channel);
|
||||
break;
|
||||
case "status":
|
||||
await HandleStatus(ctx, channel);
|
||||
break;
|
||||
default:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, $"{Config.Prefix}marriage <propose|accept|decline|status|divorce>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandlePropose(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long proposerId = ctx.Member.UserId;
|
||||
string sender = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
long? targetUserId = null;
|
||||
string? targetMention = null;
|
||||
|
||||
// Prefer reply-to for target resolution
|
||||
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)
|
||||
{
|
||||
targetUserId = author.Id;
|
||||
targetMention = $"«@u-{author.Id}»";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to message mention
|
||||
if (targetUserId is null && ctx.Message.Mentions is not null && ctx.Message.Mentions.Any())
|
||||
{
|
||||
long memberId = ctx.Message.Mentions.First().TargetId;
|
||||
var mentioned = await ctx.Planet.FetchMemberAsync(memberId);
|
||||
if (mentioned is not null)
|
||||
{
|
||||
targetUserId = mentioned.UserId;
|
||||
targetMention = $"«@u-{mentioned.UserId}»";
|
||||
}
|
||||
}
|
||||
|
||||
if (targetUserId is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Please reply to someone's message or mention them. Usage: `{Config.Prefix}marriage propose @user`");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MarriageService.Propose(proposerId, targetUserId.Value);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.ProposeResult.SelfPropose:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You can't propose to yourself!");
|
||||
break;
|
||||
case MarriageService.ProposeResult.AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"You're already married, {sender}! You'd need to `{Config.Prefix}marriage divorce` first.");
|
||||
break;
|
||||
case MarriageService.ProposeResult.TargetAlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"{targetMention} is already married!");
|
||||
break;
|
||||
case MarriageService.ProposeResult.AlreadyProposed:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"You've already proposed to {targetMention}! Waiting for their response...");
|
||||
break;
|
||||
case MarriageService.ProposeResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💍 {sender} gets down on one knee and proposes to {targetMention}!\n" +
|
||||
$"{targetMention}, type `{Config.Prefix}marriage accept` to accept or `{Config.Prefix}marriage decline` to decline.\n" +
|
||||
$"This proposal expires in 5 minutes.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleAccept(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long acceptorId = ctx.Member.UserId;
|
||||
string acceptorName = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
var (result, proposerId) = await MarriageService.AcceptAsync(acceptorId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.AcceptResult.NoPendingProposal:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
"You don't have any pending proposals to accept.");
|
||||
break;
|
||||
case MarriageService.AcceptResult.Expired:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"That proposal from «@u-{proposerId}» has expired.");
|
||||
break;
|
||||
case MarriageService.AcceptResult.AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
"One of you is already married! The proposal has been cancelled.");
|
||||
break;
|
||||
case MarriageService.AcceptResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💒 {acceptorName} has accepted «@u-{proposerId}»'s proposal! They are now married! 🎉");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleDecline(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long acceptorId = ctx.Member.UserId;
|
||||
string acceptorName = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
var (result, proposerId) = MarriageService.Decline(acceptorId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.DeclineResult.NoPendingProposal:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
"You don't have any pending proposals to decline.");
|
||||
break;
|
||||
case MarriageService.DeclineResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 {acceptorName} has declined «@u-{proposerId}»'s proposal.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleStatus(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long userId = ctx.Member.UserId;
|
||||
string name = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
|
||||
if (ctx.Message.Mentions is not null && ctx.Message.Mentions.Any())
|
||||
{
|
||||
long memberId = ctx.Message.Mentions.First().TargetId;
|
||||
var mentioned = await ctx.Planet.FetchMemberAsync(memberId);
|
||||
if (mentioned is not null)
|
||||
{
|
||||
userId = mentioned.UserId;
|
||||
name = mentioned.Nickname ?? mentioned.User?.Name ?? "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
long? partnerId = MarriageService.GetPartner(userId);
|
||||
|
||||
if (partnerId is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 {name} is not currently married.");
|
||||
}
|
||||
else
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💑 {name} is married to «@u-{partnerId}»!");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleForce(CommandContext ctx, Channel channel)
|
||||
{
|
||||
if (ctx.Member.UserId != Config.OwnerId)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You don't have permission to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
string action = ctx.Args.Length > 1 ? ctx.Args[1].ToLower() : "";
|
||||
|
||||
if (action is not ("marry" or "divorce"))
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Usage: `{Config.Prefix}marriage force marry @user1 @user2` or `{Config.Prefix}marriage force divorce @user1`");
|
||||
return;
|
||||
}
|
||||
|
||||
var mentions = ctx.Message.Mentions ?? [];
|
||||
|
||||
if (action == "marry")
|
||||
{
|
||||
if (mentions.Count < 2)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Please mention two users. Usage: `{Config.Prefix}marriage force marry @user1 @user2`");
|
||||
return;
|
||||
}
|
||||
|
||||
var member1 = await ctx.Planet.FetchMemberAsync(mentions[0].TargetId);
|
||||
var member2 = await ctx.Planet.FetchMemberAsync(mentions[1].TargetId);
|
||||
|
||||
if (member1 is null || member2 is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not find one or both of those members.");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await MarriageService.ForceMarryAsync(member1.UserId, member2.UserId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.ForceMarryResult.SameUser:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You can't marry someone to themselves!");
|
||||
break;
|
||||
case MarriageService.ForceMarryResult.User1AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"«@u-{member1.UserId}» is already married.");
|
||||
break;
|
||||
case MarriageService.ForceMarryResult.User2AlreadyMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"«@u-{member2.UserId}» is already married.");
|
||||
break;
|
||||
case MarriageService.ForceMarryResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💒 «@u-{member1.UserId}» and «@u-{member2.UserId}» are now married! 🎉");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // divorce
|
||||
{
|
||||
if (mentions.Count < 1)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Please mention a user. Usage: `{Config.Prefix}marriage force divorce @user`");
|
||||
return;
|
||||
}
|
||||
|
||||
var member = await ctx.Planet.FetchMemberAsync(mentions[0].TargetId);
|
||||
|
||||
if (member is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not find that member.");
|
||||
return;
|
||||
}
|
||||
|
||||
var (result, partnerId) = await MarriageService.DivorceAsync(member.UserId);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.DivorceResult.NotMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"«@u-{member.UserId}» is not married.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 «@u-{member.UserId}» and «@u-{partnerId}» have been forcefully divorced.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleDivorce(CommandContext ctx, Channel channel)
|
||||
{
|
||||
long userId = ctx.Member.UserId;
|
||||
string name = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
string action = ctx.Args.Length > 1 ? ctx.Args[1].ToLower() : "";
|
||||
|
||||
if (action == "cancel")
|
||||
{
|
||||
if (MarriageService.CancelDivorce(userId))
|
||||
await MessageHelper.ReplyAsync(ctx, channel, $"Divorce cancelled, {name}.");
|
||||
else
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "You don't have a pending divorce to cancel.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == "confirm")
|
||||
{
|
||||
var (result, partnerId) = await MarriageService.DivorceAsync(userId, confirmed: true);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case MarriageService.DivorceResult.NotMarried:
|
||||
await MessageHelper.ReplyAsync(ctx, channel, $"You're not married, {name}.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.NoConfirmation:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"No pending divorce found. Run `{Config.Prefix}marriage divorce` first.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.ConfirmationExpired:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Your divorce confirmation expired. Run `{Config.Prefix}marriage divorce` again to start over.");
|
||||
break;
|
||||
case MarriageService.DivorceResult.Ok:
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"💔 {name} and «@u-{partnerId}» have divorced.");
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Initial divorce request — ask for confirmation
|
||||
long? partnerId2 = MarriageService.GetPartner(userId);
|
||||
if (partnerId2 is null)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, $"You're not married, {name}.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool requested = MarriageService.RequestDivorceConfirmation(userId);
|
||||
if (requested)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel,
|
||||
$"Are you sure you want to divorce «@u-{partnerId2}»?\n" +
|
||||
$"Type `{Config.Prefix}marriage divorce confirm` within 60 seconds to confirm, or `{Config.Prefix}marriage divorce cancel` to cancel.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Nom.cs
Normal file
118
SkyBot/Commands/RP/Nom.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Nom : ICommand
|
||||
{
|
||||
public string Name => "nom";
|
||||
public string[] Aliases => ["noms"];
|
||||
public string Description => "Nom on someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "nom [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/nom");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a nom gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the nom gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "nom.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the nom gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the nom gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} noms on {target}!"
|
||||
: $"{sender} is nomming!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "nom.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Nya.cs
Normal file
118
SkyBot/Commands/RP/Nya.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Nya : ICommand
|
||||
{
|
||||
public string Name => "nya";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Nya with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "nya [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/nya");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a nya gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the nya gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "nya.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the nya gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the nya gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} nyas at {target}!"
|
||||
: $"{sender} nyas!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "nya.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,14 @@ namespace SkyBot.Commands
|
||||
public class Pat : ICommand
|
||||
{
|
||||
public string Name => "pat";
|
||||
public string[] Aliases => [];
|
||||
public string[] Aliases => ["pats"];
|
||||
public string Description => "Give someone headpats with a random gif.";
|
||||
public string Section => "Chill";
|
||||
public string Section => "RP";
|
||||
public string Usage => "pat [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0" } }
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
@@ -105,8 +105,8 @@ namespace SkyBot.Commands
|
||||
|
||||
string sender = ctx.Member.Nickname ?? ctx.Member.User?.Name ?? "Unknown";
|
||||
string text = target is not null
|
||||
? $"{sender} gives {target} headpats! 🥰"
|
||||
: $"{sender} wants headpats! 🥰";
|
||||
? $"{sender} gives {target} headpats!"
|
||||
: $"{sender} wants headpats!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
@@ -117,7 +117,7 @@ namespace SkyBot.Commands
|
||||
Height = height
|
||||
};
|
||||
|
||||
await channel.SendMessageAsync(text, attachments: [attachment]);
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Poke.cs
Normal file
118
SkyBot/Commands/RP/Poke.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Poke : ICommand
|
||||
{
|
||||
public string Name => "poke";
|
||||
public string[] Aliases => ["pokes"];
|
||||
public string Description => "Poke someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "poke [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/poke");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a poke gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the poke gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "poke.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the poke gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the poke gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} pokes {target}!"
|
||||
: $"{sender} is poking around!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "poke.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Pout.cs
Normal file
118
SkyBot/Commands/RP/Pout.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Pout : ICommand
|
||||
{
|
||||
public string Name => "pout";
|
||||
public string[] Aliases => ["pouts"];
|
||||
public string Description => "Pout with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "pout [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/pout");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a pout gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the pout gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "pout.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the pout gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the pout gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} pouts at {target}!"
|
||||
: $"{sender} is pouting!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "pout.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Punch.cs
Normal file
118
SkyBot/Commands/RP/Punch.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Punch : ICommand
|
||||
{
|
||||
public string Name => "punch";
|
||||
public string[] Aliases => ["punches"];
|
||||
public string Description => "Punch someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "punch [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/punch");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a punch gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the punch gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "punch.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the punch gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the punch gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} punches {target}!"
|
||||
: $"{sender} is throwing punches!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "punch.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Run.cs
Normal file
118
SkyBot/Commands/RP/Run.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Run : ICommand
|
||||
{
|
||||
public string Name => "run";
|
||||
public string[] Aliases => ["runs"];
|
||||
public string Description => "Run away with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "run [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/run");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a run gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the run gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "run.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the run gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the run gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} runs away from {target}!"
|
||||
: $"{sender} runs away!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "run.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Shocked.cs
Normal file
118
SkyBot/Commands/RP/Shocked.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Shocked : ICommand
|
||||
{
|
||||
public string Name => "shocked";
|
||||
public string[] Aliases => ["shock"];
|
||||
public string Description => "Express shock with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "shocked [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/shocked");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a shocked gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the shocked gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "shocked.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the shocked gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the shocked gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} is shocked by {target}!"
|
||||
: $"{sender} is shocked!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "shocked.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Sleep.cs
Normal file
118
SkyBot/Commands/RP/Sleep.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Sleep : ICommand
|
||||
{
|
||||
public string Name => "sleep";
|
||||
public string[] Aliases => ["eep"];
|
||||
public string Description => "Sleep with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "sleep [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/sleep");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a sleep gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the sleep gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "sleep.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the sleep gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the sleep gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} falls asleep on {target}!"
|
||||
: $"{sender} is sleeping! zzz...";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "sleep.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Smug.cs
Normal file
118
SkyBot/Commands/RP/Smug.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Smug : ICommand
|
||||
{
|
||||
public string Name => "smug";
|
||||
public string[] Aliases => Array.Empty<string>();
|
||||
public string Description => "Look smug with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "smug [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/smug");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a smug gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the smug gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "smug.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the smug gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the smug gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} looks smug at {target}!"
|
||||
: $"{sender} is feeling smug!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "smug.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Spin.cs
Normal file
118
SkyBot/Commands/RP/Spin.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Spin : ICommand
|
||||
{
|
||||
public string Name => "spin";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Spin with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "spin [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/spin");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a spin gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the spin gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "spin.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the spin gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the spin gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} spins {target} around!"
|
||||
: $"{sender} is spinning!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "spin.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Tableflip.cs
Normal file
118
SkyBot/Commands/RP/Tableflip.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Tableflip : ICommand
|
||||
{
|
||||
public string Name => "tableflip";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Flip a table with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "tableflip [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/tableflip");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a tableflip gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the tableflip gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "tableflip.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the tableflip gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the tableflip gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} flips the table at {target}! (╯°□°)╯︵ ┻━┻"
|
||||
: $"{sender} flips the table! (╯°□°)╯︵ ┻━┻";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "tableflip.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Teehee.cs
Normal file
118
SkyBot/Commands/RP/Teehee.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Teehee : ICommand
|
||||
{
|
||||
public string Name => "teehee";
|
||||
public string[] Aliases => ["hehe"];
|
||||
public string Description => "Teehee with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "teehee [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/teehee");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a teehee gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the teehee gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "teehee.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the teehee gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the teehee gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} teehees at {target}!"
|
||||
: $"{sender} teehees~";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "teehee.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Tickle.cs
Normal file
118
SkyBot/Commands/RP/Tickle.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Tickle : ICommand
|
||||
{
|
||||
public string Name => "tickle";
|
||||
public string[] Aliases => ["tickles"];
|
||||
public string Description => "Tickle someone with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "tickle [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/tickle");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a tickle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the tickle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "tickle.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the tickle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the tickle gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} tickles {target}!"
|
||||
: $"{sender} wants to tickle someone!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "tickle.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Wave.cs
Normal file
118
SkyBot/Commands/RP/Wave.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Wave : ICommand
|
||||
{
|
||||
public string Name => "wave";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Wave with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "wave [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/wave");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a wave gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the wave gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "wave.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the wave gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the wave gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} waves at {target}!"
|
||||
: $"{sender} waves!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "wave.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Wink.cs
Normal file
118
SkyBot/Commands/RP/Wink.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Wink : ICommand
|
||||
{
|
||||
public string Name => "wink";
|
||||
public string[] Aliases => [];
|
||||
public string Description => "Wink with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "wink [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/wink");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a wink gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the wink gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "wink.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the wink gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the wink gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} winks at {target}! ;)"
|
||||
: $"{sender} winks! ;)";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "wink.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SkyBot/Commands/RP/Yawn.cs
Normal file
118
SkyBot/Commands/RP/Yawn.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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 Yawn : ICommand
|
||||
{
|
||||
public string Name => "yawn";
|
||||
public string[] Aliases => ["yawns"];
|
||||
public string Description => "Yawn with a random gif.";
|
||||
public string Section => "RP";
|
||||
public string Usage => "yawn [@user]";
|
||||
|
||||
private static readonly HttpClient _http = new()
|
||||
{
|
||||
DefaultRequestHeaders = { { "User-Agent", "SkyBot/1.0 (https://skyjoshua.xyz, https://valour.gg)" } }
|
||||
};
|
||||
|
||||
public async Task Execute(CommandContext ctx)
|
||||
{
|
||||
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||
long channelId = ctx.ChannelId;
|
||||
|
||||
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||
|
||||
await channel.SendIsTyping();
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await _http.GetStringAsync("https://nekos.best/api/v2/yawn");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not fetch a yawn gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var results = doc.RootElement.GetProperty("results");
|
||||
string gifUrl = results[0].GetProperty("url").GetString()!;
|
||||
|
||||
byte[] gifBytes;
|
||||
try
|
||||
{
|
||||
gifBytes = await _http.GetByteArrayAsync(gifUrl);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not download the yawn gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
if (gifBytes.Length >= 10)
|
||||
{
|
||||
width = gifBytes[6] | (gifBytes[7] << 8);
|
||||
height = gifBytes[8] | (gifBytes[9] << 8);
|
||||
}
|
||||
|
||||
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", "yawn.gif");
|
||||
|
||||
var uploadResult = await ctx.Planet.Node.PostMultipartDataWithResponse<string>("upload/image", form);
|
||||
if (!uploadResult.Success)
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the yawn gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
cdnUrl = uploadResult.Data!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageHelper.ReplyAsync(ctx, channel, "Could not upload the yawn gif. Try again later.");
|
||||
return;
|
||||
}
|
||||
|
||||
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} yawns at {target}!"
|
||||
: $"{sender} is yawning!";
|
||||
|
||||
var attachment = new MessageAttachment(MessageAttachmentType.Image)
|
||||
{
|
||||
Location = cdnUrl,
|
||||
MimeType = "image/gif",
|
||||
FileName = "yawn.gif",
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
await MessageHelper.ReplyAsync(ctx, channel, text, [attachment]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@ namespace SkyBot
|
||||
public static class Config {
|
||||
public static readonly long OwnerId = 15652354820931584;
|
||||
public static readonly string Prefix = "s/";
|
||||
public static readonly string SourceLink = "https://github.com/SkyJoshua/SkyBot";
|
||||
public static readonly string SourceLink = "https://git.skyjoshua.xyz/SkyJoshua/SkyBot";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
SkyBot/Helpers/DatabaseHelper.cs
Normal file
49
SkyBot/Helpers/DatabaseHelper.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace SkyBot.Helpers
|
||||
{
|
||||
public static class DatabaseHelper
|
||||
{
|
||||
private const string ConnectionString = "Data Source=database.db";
|
||||
|
||||
public static SqliteConnection GetConnection()
|
||||
{
|
||||
SqliteConnection connection = new SqliteConnection(ConnectionString);
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
|
||||
public static async Task InitializeAsync()
|
||||
{
|
||||
using SqliteConnection connection = GetConnection();
|
||||
|
||||
using (SqliteCommand cmd = connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = @"
|
||||
CREATE TABLE IF NOT EXISTS WelcomeConfigs (
|
||||
PlanetId INTEGER PRIMARY KEY,
|
||||
ChannelId INTEGER NOT NULL DEFAULT 0,
|
||||
Message TEXT NOT NULL DEFAULT 'Welcome to the planet, {username}!',
|
||||
Active INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
using (SqliteCommand cmd = connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = @"
|
||||
CREATE TABLE IF NOT EXISTS Marriages (
|
||||
UserId1 INTEGER NOT NULL,
|
||||
UserId2 INTEGER NOT NULL,
|
||||
MarriedAt TEXT NOT NULL,
|
||||
PRIMARY KEY (UserId1, UserId2)
|
||||
);
|
||||
";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
Console.WriteLine("Database initialized.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,24 @@ namespace SkyBot.Helpers
|
||||
return await ctx.Client.MessageService.SendMessage(msg);
|
||||
}
|
||||
|
||||
public static async Task<TaskResult<Message>> ReplyAsync(CommandContext ctx, Channel channel, string content, List<MessageAttachment> attachments)
|
||||
{
|
||||
long? replyToId = ctx.Message.ReplyToId.HasValue ? ctx.Message.ReplyToId : ctx.Message.Id;
|
||||
|
||||
var msg = new Message(ctx.Client)
|
||||
{
|
||||
Content = content,
|
||||
ChannelId = channel.Id,
|
||||
PlanetId = ctx.Planet.Id,
|
||||
AuthorUserId = ctx.Client.Me.Id,
|
||||
AuthorMemberId = channel.Planet?.MyMember.Id,
|
||||
ReplyToId = replyToId,
|
||||
Fingerprint = Guid.NewGuid().ToString()
|
||||
};
|
||||
msg.SetAttachments(attachments);
|
||||
return await ctx.Client.MessageService.SendMessage(msg);
|
||||
}
|
||||
|
||||
public static async Task<TaskResult<Message>> EditAsync(Channel channel, Message message, string content)
|
||||
{
|
||||
message.Content = content;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace SkyBot.Helpers
|
||||
{
|
||||
public static class DatabaseHelper
|
||||
{
|
||||
private const string ConnectionString = "Data Source=database.db";
|
||||
|
||||
public static SqliteConnection GetConnection()
|
||||
{
|
||||
SqliteConnection connection = new SqliteConnection(ConnectionString);
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
|
||||
public static async Task InitializeAsync()
|
||||
{
|
||||
using SqliteConnection connection = GetConnection();
|
||||
using SqliteCommand cmd = connection.CreateCommand();
|
||||
cmd.CommandText = @"
|
||||
CREATE TABLE IF NOT EXISTS WelcomeConfigs (
|
||||
PlanetId INTEGER PRIMARY KEY,
|
||||
ChannelId INTEGER NOT NULL DEFAULT 0,
|
||||
Message TEXT NOT NULL DEFAULT 'Welcome to the planet, {username}!',
|
||||
Active INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
Console.WriteLine("Database initialized.");
|
||||
}
|
||||
}
|
||||
}
|
||||
203
SkyBot/Services/MarriageService.cs
Normal file
203
SkyBot/Services/MarriageService.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SkyBot.Helpers;
|
||||
|
||||
namespace SkyBot.Services
|
||||
{
|
||||
public static class MarriageService
|
||||
{
|
||||
// userId -> partnerId (stored in both directions)
|
||||
private static readonly ConcurrentDictionary<long, long> _marriages = new();
|
||||
|
||||
// targetId -> (proposerId, expiresAt)
|
||||
private static readonly ConcurrentDictionary<long, (long ProposerId, DateTime ExpiresAt)> _pendingProposals = new();
|
||||
|
||||
private static readonly TimeSpan ProposalTimeout = TimeSpan.FromMinutes(5);
|
||||
|
||||
public static async Task InitializeAsync()
|
||||
{
|
||||
using SqliteConnection connection = DatabaseHelper.GetConnection();
|
||||
using SqliteCommand cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "SELECT UserId1, UserId2 FROM Marriages";
|
||||
using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
long u1 = (long)reader["UserId1"];
|
||||
long u2 = (long)reader["UserId2"];
|
||||
_marriages[u1] = u2;
|
||||
_marriages[u2] = u1;
|
||||
}
|
||||
Console.WriteLine($"MarriageService initialized. Loaded {_marriages.Count / 2} marriages from database.");
|
||||
}
|
||||
|
||||
public static long? GetPartner(long userId)
|
||||
{
|
||||
return _marriages.TryGetValue(userId, out long partnerId) ? partnerId : null;
|
||||
}
|
||||
|
||||
public enum ProposeResult
|
||||
{
|
||||
Ok,
|
||||
SelfPropose,
|
||||
AlreadyMarried,
|
||||
TargetAlreadyMarried,
|
||||
AlreadyProposed
|
||||
}
|
||||
|
||||
public static ProposeResult Propose(long proposerId, long targetId)
|
||||
{
|
||||
if (proposerId == targetId) return ProposeResult.SelfPropose;
|
||||
if (_marriages.ContainsKey(proposerId)) return ProposeResult.AlreadyMarried;
|
||||
if (_marriages.ContainsKey(targetId)) return ProposeResult.TargetAlreadyMarried;
|
||||
|
||||
// Check if there's already a non-expired proposal from this proposer to this target
|
||||
if (_pendingProposals.TryGetValue(targetId, out var existing)
|
||||
&& existing.ProposerId == proposerId
|
||||
&& existing.ExpiresAt > DateTime.UtcNow)
|
||||
return ProposeResult.AlreadyProposed;
|
||||
|
||||
_pendingProposals[targetId] = (proposerId, DateTime.UtcNow.Add(ProposalTimeout));
|
||||
return ProposeResult.Ok;
|
||||
}
|
||||
|
||||
public enum AcceptResult
|
||||
{
|
||||
Ok,
|
||||
NoPendingProposal,
|
||||
Expired,
|
||||
AlreadyMarried
|
||||
}
|
||||
|
||||
public static async Task<(AcceptResult Result, long ProposerId)> AcceptAsync(long acceptorId)
|
||||
{
|
||||
if (!_pendingProposals.TryRemove(acceptorId, out var proposal))
|
||||
return (AcceptResult.NoPendingProposal, 0);
|
||||
|
||||
if (proposal.ExpiresAt <= DateTime.UtcNow)
|
||||
return (AcceptResult.Expired, proposal.ProposerId);
|
||||
|
||||
if (_marriages.ContainsKey(acceptorId) || _marriages.ContainsKey(proposal.ProposerId))
|
||||
return (AcceptResult.AlreadyMarried, proposal.ProposerId);
|
||||
|
||||
long u1 = Math.Min(acceptorId, proposal.ProposerId);
|
||||
long u2 = Math.Max(acceptorId, proposal.ProposerId);
|
||||
string marriedAt = DateTime.UtcNow.ToString("o");
|
||||
|
||||
using SqliteConnection connection = DatabaseHelper.GetConnection();
|
||||
using SqliteCommand cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "INSERT OR IGNORE INTO Marriages (UserId1, UserId2, MarriedAt) VALUES ($u1, $u2, $at)";
|
||||
cmd.Parameters.AddWithValue("$u1", u1);
|
||||
cmd.Parameters.AddWithValue("$u2", u2);
|
||||
cmd.Parameters.AddWithValue("$at", marriedAt);
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
_marriages[acceptorId] = proposal.ProposerId;
|
||||
_marriages[proposal.ProposerId] = acceptorId;
|
||||
|
||||
return (AcceptResult.Ok, proposal.ProposerId);
|
||||
}
|
||||
|
||||
public enum DeclineResult
|
||||
{
|
||||
Ok,
|
||||
NoPendingProposal
|
||||
}
|
||||
|
||||
public static (DeclineResult Result, long ProposerId) Decline(long acceptorId)
|
||||
{
|
||||
if (!_pendingProposals.TryRemove(acceptorId, out var proposal))
|
||||
return (DeclineResult.NoPendingProposal, 0);
|
||||
|
||||
return (DeclineResult.Ok, proposal.ProposerId);
|
||||
}
|
||||
|
||||
public enum ForceMarryResult
|
||||
{
|
||||
Ok,
|
||||
SameUser,
|
||||
User1AlreadyMarried,
|
||||
User2AlreadyMarried
|
||||
}
|
||||
|
||||
public static async Task<ForceMarryResult> ForceMarryAsync(long userId1, long userId2)
|
||||
{
|
||||
if (userId1 == userId2) return ForceMarryResult.SameUser;
|
||||
if (_marriages.ContainsKey(userId1)) return ForceMarryResult.User1AlreadyMarried;
|
||||
if (_marriages.ContainsKey(userId2)) return ForceMarryResult.User2AlreadyMarried;
|
||||
|
||||
long u1 = Math.Min(userId1, userId2);
|
||||
long u2 = Math.Max(userId1, userId2);
|
||||
string marriedAt = DateTime.UtcNow.ToString("o");
|
||||
|
||||
using SqliteConnection connection = DatabaseHelper.GetConnection();
|
||||
using SqliteCommand cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "INSERT OR IGNORE INTO Marriages (UserId1, UserId2, MarriedAt) VALUES ($u1, $u2, $at)";
|
||||
cmd.Parameters.AddWithValue("$u1", u1);
|
||||
cmd.Parameters.AddWithValue("$u2", u2);
|
||||
cmd.Parameters.AddWithValue("$at", marriedAt);
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
_marriages[userId1] = userId2;
|
||||
_marriages[userId2] = userId1;
|
||||
|
||||
return ForceMarryResult.Ok;
|
||||
}
|
||||
|
||||
// userId -> confirmationExpiresAt
|
||||
private static readonly ConcurrentDictionary<long, DateTime> _pendingDivorces = new();
|
||||
|
||||
private static readonly TimeSpan DivorceConfirmTimeout = TimeSpan.FromSeconds(60);
|
||||
|
||||
public static bool RequestDivorceConfirmation(long userId)
|
||||
{
|
||||
if (!_marriages.ContainsKey(userId)) return false;
|
||||
_pendingDivorces[userId] = DateTime.UtcNow.Add(DivorceConfirmTimeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CancelDivorce(long userId)
|
||||
{
|
||||
return _pendingDivorces.TryRemove(userId, out _);
|
||||
}
|
||||
|
||||
public enum DivorceResult
|
||||
{
|
||||
Ok,
|
||||
NotMarried,
|
||||
NoConfirmation,
|
||||
ConfirmationExpired
|
||||
}
|
||||
|
||||
public static async Task<(DivorceResult Result, long PartnerId)> DivorceAsync(long userId, bool confirmed = false)
|
||||
{
|
||||
if (!_marriages.ContainsKey(userId))
|
||||
return (DivorceResult.NotMarried, 0);
|
||||
|
||||
if (!confirmed)
|
||||
return (DivorceResult.NoConfirmation, 0);
|
||||
|
||||
if (!_pendingDivorces.TryRemove(userId, out var expiresAt))
|
||||
return (DivorceResult.NoConfirmation, 0);
|
||||
|
||||
if (expiresAt <= DateTime.UtcNow)
|
||||
return (DivorceResult.ConfirmationExpired, 0);
|
||||
|
||||
if (!_marriages.TryRemove(userId, out long partnerId))
|
||||
return (DivorceResult.NotMarried, 0);
|
||||
|
||||
_marriages.TryRemove(partnerId, out _);
|
||||
|
||||
long u1 = Math.Min(userId, partnerId);
|
||||
long u2 = Math.Max(userId, partnerId);
|
||||
|
||||
using SqliteConnection connection = DatabaseHelper.GetConnection();
|
||||
using SqliteCommand cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "DELETE FROM Marriages WHERE UserId1 = $u1 AND UserId2 = $u2";
|
||||
cmd.Parameters.AddWithValue("$u1", u1);
|
||||
cmd.Parameters.AddWithValue("$u2", u2);
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
return (DivorceResult.Ok, partnerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
|
||||
using SkyBot.Commands;
|
||||
using SkyBot.Helpers;
|
||||
using SkyBot.Models;
|
||||
using SkyBot.Services;
|
||||
using Valour.Sdk.Client;
|
||||
using Valour.Sdk.Models;
|
||||
|
||||
@@ -13,6 +14,7 @@ namespace SkyBot.Services.Messages
|
||||
{
|
||||
private static readonly ConcurrentDictionary<long, DateTime> _cooldowns = new();
|
||||
private static readonly TimeSpan _cooldown = TimeSpan.FromSeconds(2);
|
||||
public static readonly HashSet<long> disabledChannels = new();
|
||||
public static async Task MessageAsync(
|
||||
ValourClient client,
|
||||
ConcurrentDictionary<long, Channel> channelCache,
|
||||
@@ -59,6 +61,9 @@ namespace SkyBot.Services.Messages
|
||||
|
||||
_cooldowns[message.AuthorUserId] = DateTime.UtcNow;
|
||||
|
||||
if (disabledChannels.Contains(channelId) && command != "channel" && command != "ch")
|
||||
return;
|
||||
|
||||
if (CommandRegistry.Commands.TryGetValue(command, out var handler))
|
||||
{
|
||||
await handler.Execute(ctx);
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace SkyBot
|
||||
StartTime = DateTime.UtcNow;
|
||||
await DatabaseHelper.InitializeAsync();
|
||||
await WelcomeService.InitializeAsync();
|
||||
await MarriageService.InitializeAsync();
|
||||
await BotService.InitializeBotAsync(_client, _channelCache, _initializedPlanets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.2.2.1</Version>
|
||||
<Version>0.2.3.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNetEnv" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.5" />
|
||||
<PackageReference Include="Valour.Sdk" Version="0.5.19" />
|
||||
<PackageReference Include="Valour.Sdk" Version="0.5.20" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user