Skip to content

Instantly share code, notes, and snippets.

@nathan130200
Last active July 20, 2019 23:24
Show Gist options
  • Save nathan130200/a2e791cc28dfb10cd9e15d3f30fe6b80 to your computer and use it in GitHub Desktop.
Save nathan130200/a2e791cc28dfb10cd9e15d3f30fe6b80 to your computer and use it in GitHub Desktop.
C# Discord application assets downloader using D#+. (produces a ZIP file with all assets files)
public delegate Task ApplicationAssetsDownloaderEventHandler(DiscordApplicationAsset asset);
class ApplicationAssetsDownloader : IDisposable
{
private ConcurrentQueue<DiscordApplicationAsset> _a;
private ZipArchive _ar;
private MemoryStream _ms;
public event ApplicationAssetsDownloaderEventHandler DownloadStartedAsync;
public event ApplicationAssetsDownloaderEventHandler DownloadCompletedAsync;
public event ApplicationAssetsDownloaderEventHandler DownloadFailedAsync;
public ApplicationAssetsDownloader(IReadOnlyList<DiscordApplicationAsset> assets)
{
_a = new ConcurrentQueue<DiscordApplicationAsset>(assets);
_ms = new MemoryStream();
_ar = new ZipArchive(_ms, ZipArchiveMode.Create);
}
public async Task<MemoryStream> DownloadAsync()
{
while (!_a.IsEmpty)
{
if (_a.TryDequeue(out var aps))
{
try { await DownloadStartedAsync?.Invoke(aps); }
catch { /* ignored */ }
using (var client = new HttpClient())
{
try
{
var res = await client.GetAsync(aps.Url);
if (res.IsSuccessStatusCode)
{
var entry = _ar.CreateEntry(aps.Application.Id + "/" + aps.Name + ".png");
using (var es = entry.Open()) // es = Entry Strema
using (var cs = await res.Content.ReadAsStreamAsync()) // cs = Content Stream
cs.CopyTo(es);
_ms.Flush();
try { await DownloadCompletedAsync?.Invoke(aps); }
catch { /* ignored */ }
}
}
catch
{
try { await DownloadFailedAsync?.Invoke(aps); }
catch { /* ignored */ }
}
}
}
}
_ms.Flush();
_ms.Position = 0;
return _ms;
}
void IDisposable.Dispose()
{
_a.Clear();
_a = null;
_ms = null;
_ar = null;
}
}
public class DiscordMessageQueue : IDisposable
{
private DiscordChannel _channel;
private ConcurrentBag<DiscordMessage> _messages;
public DiscordMessageQueue(DiscordChannel channel)
{
_channel = channel;
_messages = new ConcurrentBag<DiscordMessage>();
}
public async Task<DiscordMessage> C(string content = null, bool tts = false, DiscordEmbedBuilder embed = null)
{
var msg = await _channel.SendMessageAsync(content, tts, embed);
_messages.Add(msg);
return msg;
}
public async Task CleanupAsync()
{
await _channel.DeleteMessagesAsync(_messages);
}
void IDisposable.Dispose()
{
_messages = null;
_channel = null;
}
public static DiscordMessageQueue operator *(DiscordMessageQueue mq, DiscordMessage m)
{
mq._messages.Add(m);
return mq;
}
public static DiscordMessageQueue operator *(DiscordMessageQueue mq, string content)
{
return Task.Run(async () => await mq.C(content))
.ContinueWith(t => mq)
.GetAwaiter()
.GetResult();
}
public static DiscordMessageQueue operator *(DiscordMessageQueue mq, (string content, DiscordEmbedBuilder embed) vt)
{
return Task.Run(async () => await mq.C(content: vt.content, embed: vt.embed))
.ContinueWith(t => mq)
.GetAwaiter()
.GetResult();
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.CommandsNext.Attributes;
using DSharpPlus.Entities;
using DSharpPlus.Interactivity;
namespace CSharp.Modules
{
[Group]
public class DiscordModule : BaseCommandModule
{
[Command]
public async Task Assets(CommandContext ctx)
{
var assets = await ctx.Client.CurrentApplication.GetAssetsAsync();
var (a, b, c) = (
DiscordEmoji.FromGuildEmote(ctx.Client, 594249301295890463),
DiscordEmoji.FromGuildEmote(ctx.Client, 452529445899730945),
DiscordEmoji.FromGuildEmote(ctx.Client, 452529446214172692)
);
var mq = new DiscordMessageQueue(ctx.Channel);
var state = null as DiscordMessage;
var stream = null as MemoryStream;
var downloader = new ApplicationAssetsDownloader(assets);
var error = false;
downloader.DownloadStartedAsync += async e =>
{
mq *= state = await ctx.RespondAsync($"{ctx.User.Mention} {a} Asset **{e.Name}** (`{e.Id}`)...");
};
downloader.DownloadFailedAsync += async e =>
{
if (!error) error = true;
await state?.ModifyAsync($"{ctx.User.Mention} {c} Asset **{e.Name}** (`{e.Id}`) failed to download.");
};
downloader.DownloadCompletedAsync += async e =>
{
await state?.ModifyAsync($"{ctx.User.Mention} {b} Asset **{e.Name}** (`{e.Id}`) saved to archive.");
};
stream = await downloader.DownloadAsync();
using (downloader)
using (stream)
{
mq *= await ctx.RespondAsync($"{ctx.User.Mention} <:issueclosed:527890674822021137> Assets loaded" + (error ? " with errors." : "."));
var w = await ctx.RespondAsync($"{ctx.User.Mention} <:issue:527890675182731274> Uploading archive.");
await ctx.RespondWithFileAsync("assets.zip´", stream, ctx.User.Mention);
}
await mq.CleanupAsync();
mq = null;
stream = null;
downloader = null;
assets = null;
state = null;
error = default;
}
}
}
[Command]
public async Task Assets(CommandContext ctx)
{
var assets = await ctx.Client.CurrentApplication.GetAssetsAsync();
var downloader = new ApplicationAssetsDownloader(assets);
var result = await downloader.DownloadAsync();
// downloader.DownloadStartedAsync: notify when asset download started.
// downloader.DownloadFailedAsync: notify when asset download failed.
// downloader.DownloadCompletedAsync: notify when asset download finished.
using (result)
using (downloader)
await ctx.RespondWithFileAsync("assets.zip", result, ctx.User.Mention); // send file to discord as assets.zip
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment