11using Discord ;
22using Discord . Interactions ;
3+ using ImageMagick ;
34using NetVips ;
45using NLog ;
56using System ;
@@ -16,7 +17,7 @@ public partial class MediaManipulationModule : InteractionModuleBase
1617 [ SlashCommand ( "caption" , "Add a caption to a piece of media" ) ]
1718 public async Task Caption ( string caption ,
1819 IAttachment attachment ,
19- [ Discord . Interactions . Summary ( description : "Export as a GIF" ) ]
20+ [ Summary ( description : "Export as a GIF" ) ]
2021 bool saveAsGif = false )
2122 {
2223 await Context . Interaction . DeferAsync ( ) ;
@@ -67,7 +68,7 @@ public async Task Caption(string caption,
6768
6869 // Create caption text
6970 var text = NVImage . Text (
70- text : $ "<span background=\" white\" >{ caption } </span>",
71+ text : $ "<span background=\" white\" >" + caption . Replace ( " \\ n" , " \n " ) + " </span>",
7172 rgba : true ,
7273 align : Enums . Align . Centre ,
7374 font : $ "FuturaExtraBlackCondensed { fontSize } px",
@@ -107,25 +108,28 @@ public async Task Caption(string caption,
107108 if ( nPages > 1 )
108109 i . Set ( "page-height" , pageHeight + textHeight ) ;
109110 } ) ;
110- Log . Debug ( "Complete" ) ;
111- if ( isAnimated || saveAsGif )
112- {
113- using var gifStream = new MemoryStream ( final . GifsaveBuffer ( dither : 1 , bitdepth : 8 , interlace : true ) ) ;
114- await FollowupWithFileAsync ( gifStream , $ "{ Context . Interaction . Id } .gif") ;
115- }
116- else
111+
112+ using var resultStream = new MemoryStream ( isAnimated || saveAsGif
113+ ? final . GifsaveBuffer ( dither : 1 , effort : 2 , bitdepth : 8 , interlace : true , reuse : true )
114+ : final . PngsaveBuffer ( compression : 4 , dither : 1 , bitdepth : 8 , interlace : true ) ) ;
115+ var filename = isAnimated || saveAsGif ? $ "{ Context . Interaction . Id } .gif" : $ "{ Context . Interaction . Id } .png";
116+ var optimizer = new ImageOptimizer ( )
117117 {
118- using var pngStream = new MemoryStream ( final . PngsaveBuffer ( compression : 4 , dither : 1 , bitdepth : 8 , interlace : true ) ) ;
119- await FollowupWithFileAsync ( pngStream , $ "{ Context . Interaction . Id } .png") ;
120- }
118+ IgnoreUnsupportedFormats = true ,
119+ OptimalCompression = true
120+ } ;
121+ resultStream . Seek ( 0 , SeekOrigin . Begin ) ;
122+ optimizer . Compress ( resultStream ) ;
123+ resultStream . Seek ( 0 , SeekOrigin . Begin ) ;
124+ Log . Debug ( $ "Uploading file \" { filename } \" ({ resultStream . Length } b)") ;
125+ await FollowupWithFileAsync ( resultStream , filename ) ;
121126 }
122127 catch ( Exception ex )
123128 {
124129 Log . Error ( ex ) ;
125130 embed . WithDescription ( $ "Failed to run task.\n ```\n { ex . Message } \n ```") . WithColor ( Color . Red ) ;
126131 await FollowupAsync ( embed : embed . Build ( ) ) ;
127132 await DiscordHelper . ReportError ( ex , Context ) ;
128- return ;
129133 }
130134 }
131135
@@ -257,11 +261,11 @@ public async Task<NVImage> Watermark(NVImage source,
257261
258262 [ SlashCommand ( "speechbubble" , "Add a speech bubble to an image or a gif." ) ]
259263 public async Task SpeechBubble ( IAttachment attachment ,
260- [ Discord . Interactions . Summary ( description : "When True, the speech bubble will be on the bottom, and when False it will be on top." ) ]
264+ [ Summary ( description : "When True, the speech bubble will be on the bottom, and when False it will be on top." ) ]
261265 bool flip = false ,
262- [ Discord . Interactions . Summary ( description : "When True, the speech bubble will have a transparent background, or it will have a white background" ) ]
266+ [ Summary ( description : "When True, the speech bubble will have a transparent background, or it will have a white background" ) ]
263267 bool alpha = false ,
264- [ Discord . Interactions . Summary ( description : "Export as a GIF" ) ]
268+ [ Summary ( description : "Export as a GIF" ) ]
265269 bool saveAsGif = false )
266270 {
267271 await Context . Interaction . DeferAsync ( ) ;
@@ -307,27 +311,29 @@ public async Task SpeechBubble(IAttachment attachment,
307311 : NVImage . NewFromStream ( MediaResources . ImageSpeechBubble ) ) ;
308312 var final = await Watermark (
309313 sourceImage , watermark , 2 , isAnimated , resize : true , yscale : 0.2f , alpha : alpha , flip : flip ) ;
310-
311-
312- Log . Debug ( $ "Complete") ;
313- if ( isAnimated || saveAsGif )
314- {
315- using var gifStream = new MemoryStream ( final . GifsaveBuffer ( dither : 1 , bitdepth : 8 , interlace : true ) ) ;
316- await FollowupWithFileAsync ( gifStream , $ "{ Context . Interaction . Id } .gif") ;
317- }
318- else
314+
315+
316+ using var resultStream = new MemoryStream ( isAnimated || saveAsGif
317+ ? final . GifsaveBuffer ( dither : 1 , effort : 2 , bitdepth : 8 , interlace : true , reuse : true )
318+ : final . PngsaveBuffer ( compression : 4 , dither : 1 , bitdepth : 8 , interlace : true ) ) ;
319+ var filename = isAnimated || saveAsGif ? $ "{ Context . Interaction . Id } .gif" : $ "{ Context . Interaction . Id } .png";
320+ var optimizer = new ImageOptimizer ( )
319321 {
320- using var pngStream = new MemoryStream ( final . PngsaveBuffer ( compression : 4 , dither : 1 , bitdepth : 8 , interlace : true ) ) ;
321- await FollowupWithFileAsync ( pngStream , $ "{ Context . Interaction . Id } .png") ;
322- }
322+ IgnoreUnsupportedFormats = true ,
323+ OptimalCompression = true
324+ } ;
325+ resultStream . Seek ( 0 , SeekOrigin . Begin ) ;
326+ optimizer . Compress ( resultStream ) ;
327+ resultStream . Seek ( 0 , SeekOrigin . Begin ) ;
328+ Log . Debug ( $ "Uploading file \" { filename } \" ({ resultStream . Length } b)") ;
329+ await FollowupWithFileAsync ( resultStream , filename ) ;
323330 }
324331 catch ( Exception ex )
325332 {
326333 Log . Error ( ex ) ;
327334 embed . WithDescription ( $ "Failed to run task.\n ```\n { ex . Message } \n ```") . WithColor ( Color . Red ) ;
328335 await FollowupAsync ( embed : embed . Build ( ) ) ;
329336 await DiscordHelper . ReportError ( ex , Context ) ;
330- return ;
331337 }
332338 }
333339}
0 commit comments