2023-03-15 14:09:09 +00:00
import Command from "./command.js" ;
import imageDetect from "../utils/imagedetect.js" ;
import { runImageJob } from "../utils/image.js" ;
import { runningCommands } from "../utils/collections.js" ;
import { readFileSync } from "fs" ;
const { emotes } = JSON . parse ( readFileSync ( new URL ( "../config/messages.json" , import . meta . url ) ) ) ;
import { random } from "../utils/misc.js" ;
import { selectedImages } from "../utils/collections.js" ;
class ImageCommand extends Command {
async criteria ( ) {
return true ;
}
async run ( ) {
this . success = false ;
const timestamp = this . type === "classic" ? this . message . createdAt : Math . floor ( ( this . interaction . id / 4194304 ) + 1420070400000 ) ;
// check if this command has already been run in this channel with the same arguments, and we are awaiting its result
// if so, don't re-run it
if ( runningCommands . has ( this . author ) && ( new Date ( runningCommands . get ( this . author ) ) - new Date ( timestamp ) ) < 5000 ) {
return "Please slow down a bit." ;
}
// before awaiting the command result, add this command to the set of running commands
runningCommands . set ( this . author , timestamp ) ;
const imageParams = {
cmd : this . constructor . command ,
params : {
togif : ! ! this . options . togif
} ,
id : this . message . event _id
} ;
2023-03-17 00:23:01 +00:00
// if (this.type === "application") // await this.acknowledge();
2023-03-15 14:09:09 +00:00
if ( this . constructor . requiresImage ) {
try {
const selection = selectedImages . get ( this . author ) ;
const image = await imageDetect ( this . client , this . message , this . interaction , this . options , true ) ;
if ( selection ) selectedImages . delete ( this . author ) ;
if ( image === undefined ) {
runningCommands . delete ( this . author ) ;
2023-03-17 00:40:15 +00:00
return ` ${ this . constructor . noImage } (Tip: try running your command while replying to an image (or link to an image)) ` ;
2023-03-15 14:09:09 +00:00
} else if ( image . type === "large" ) {
runningCommands . delete ( this . author ) ;
return "That image is too large (>= 25MB)! Try using a smaller image." ;
} else if ( image . type === "tenorlimit" ) {
runningCommands . delete ( this . author ) ;
return "I've been rate-limited by Tenor. Please try uploading your GIF elsewhere." ;
}
imageParams . path = image . path ;
imageParams . params . type = image . type ;
imageParams . url = image . url ; // technically not required but can be useful for text filtering
imageParams . name = image . name ;
if ( this . constructor . requiresGIF ) imageParams . onlyGIF = true ;
} catch ( e ) {
runningCommands . delete ( this . author ) ;
throw e ;
}
}
if ( this . constructor . requiresText ) {
const text = this . options . text ? ? this . args . join ( " " ) . trim ( ) ;
if ( text . length === 0 || ! await this . criteria ( text , imageParams . url ) ) {
runningCommands . delete ( this . author ) ;
return this . constructor . noText ;
}
}
if ( typeof this . params === "function" ) {
Object . assign ( imageParams . params , this . params ( imageParams . url , imageParams . name ) ) ;
} else if ( typeof this . params === "object" ) {
Object . assign ( imageParams . params , this . params ) ;
}
let status ;
if ( imageParams . params . type === "image/gif" && this . type === "classic" ) {
2023-03-17 01:19:39 +00:00
status = await this . processMessage ( this . message . room _id ) ;
2023-03-15 14:09:09 +00:00
}
try {
2023-03-19 08:40:32 +00:00
const { buffer , type , width , height } = await runImageJob ( imageParams ) ;
2023-03-15 14:09:09 +00:00
if ( type === "nogif" && this . constructor . requiresGIF ) {
return "That isn't a GIF!" ;
}
this . success = true ;
return {
contents : buffer ,
2023-03-19 08:40:32 +00:00
name : ` ${ this . constructor . command } . ${ type } ` ,
type ,
width ,
height
2023-03-15 14:09:09 +00:00
} ;
} catch ( e ) {
if ( e === "Request ended prematurely due to a closed connection" ) return "This image job couldn't be completed because the server it was running on went down. Try running your command again." ;
if ( e === "Job timed out" || e === "Timeout" ) return "The image is taking too long to process (>=15 minutes), so the job was cancelled. Try using a smaller image." ;
if ( e === "No available servers" ) return "I can't seem to contact the image servers, they might be down or still trying to start up. Please wait a little bit." ;
throw e ;
} finally {
try {
2023-03-17 01:19:39 +00:00
if ( status ) {
// TODO: tell status message to self-destruct
}
2023-03-15 14:09:09 +00:00
} catch {
// no-op
}
runningCommands . delete ( this . author ) ;
}
}
2023-03-17 01:19:39 +00:00
async processMessage ( channel ) {
2023-03-15 14:09:09 +00:00
this . client . send
const content = {
body : "Processing... This might take a while" ,
msgtype : "m.text" ,
} ;
2023-03-17 01:19:39 +00:00
return await this . client . sendEvent ( channel , "m.room.message" , content , "" , ( err , res ) => {
2023-03-15 14:09:09 +00:00
logger . log ( "error" , err )
} ) ;
}
static init ( ) {
this . flags = [ ] ;
if ( this . requiresText || this . textOptional ) {
this . flags . push ( {
name : "text" ,
type : 3 ,
description : "The text to put on the image" ,
required : ! this . textOptional
} ) ;
}
if ( this . requiresImage ) {
this . flags . push ( {
name : "image" ,
type : 11 ,
description : "An image/GIF attachment"
} , {
name : "link" ,
type : 3 ,
description : "An image/GIF URL"
} ) ;
}
this . flags . push ( {
name : "togif" ,
type : 5 ,
description : "Force GIF output"
} ) ;
return this ;
}
static allowedFonts = [ "futura" , "impact" , "helvetica" , "arial" , "roboto" , "noto" , "times" , "comic sans ms" ] ;
static requiresImage = true ;
static requiresText = false ;
static textOptional = false ;
static requiresGIF = false ;
static noImage = "You need to provide an image/GIF!" ;
static noText = "You need to provide some text!" ;
static command = "" ;
}
export default ImageCommand ;