@@ -11,11 +11,8 @@ import { Virustotal } from "../../../infra/virustotal.js";
1111import Mute from "./moderation/mute.js" ;
1212import { delay , unwrap } from "../../../utils/misc.js" ;
1313import { CommandSetBuilder } from "../../../command-abstractions/command-set-builder.js" ;
14- import { M } from "../../../utils/debugging-and-logging.js" ;
1514
1615const ACTION_THRESHOLD = 5 ;
17- const BASE_RETRY_DELAY_MS = 1000 ;
18- const NOT_FOUND_RETRY_DELAY_MS = 3000 ;
1916
2017class HTTPError extends Error {
2118 constructor (
@@ -160,53 +157,21 @@ export default class AntiExecutable extends BotComponent {
160157 return true ;
161158 }
162159
163- private async fetch_with_retry (
164- url : string ,
165- options ?: { limit ?: number ; max_retries ?: number } ,
166- ) : Promise < Buffer | null > {
167- // Try to fetch, retrying on 5xx failure. If the error is 404, retry once.
168- const { limit, max_retries = 3 } = options ?? { } ;
169- let attempt = 0 ;
170- let not_found_retry_used = false ;
171-
172- while ( true ) {
160+ private async fetch_with_retry ( url : string , limit ?: number , max_retries = 3 ) : Promise < Buffer > {
161+ const base_delay_ms = 1000 ;
162+ for ( let attempt = 0 ; attempt <= max_retries ; attempt ++ ) {
173163 try {
174164 return await this . fetch ( url , limit ) ;
175165 } catch ( e ) {
176- if ( e instanceof HTTPError && e . status_code === 404 ) {
177- if ( not_found_retry_used ) {
178- return null ;
179- }
180- not_found_retry_used = true ;
181- await delay ( NOT_FOUND_RETRY_DELAY_MS ) ;
182- continue ;
183- }
184- if ( attempt >= max_retries || ! this . is_retryable_error ( e ) ) {
166+ const is_last_attempt = attempt === max_retries ;
167+ if ( is_last_attempt || ! this . is_retryable_error ( e ) ) {
185168 throw e ;
186169 }
187- await delay ( BASE_RETRY_DELAY_MS * Math . pow ( 2 , attempt ) ) ;
188- attempt ++ ;
189- }
190- }
191- }
192-
193- private async fetch_attachment ( url : string , message_url : string , limit ?: number ) : Promise < Buffer | null > {
194- try {
195- const buffer = await this . fetch_with_retry ( url , { limit } ) ;
196- if ( buffer === null ) {
197- M . info ( `HTTP 404 while fetching attachment for message ${ message_url } : \`${ url } \`` ) ;
198- }
199- return buffer ;
200- } catch ( e ) {
201- if ( e instanceof HTTPError ) {
202- this . wheatley . warn (
203- `HTTP Error ${ e . status_code } while fetching attachment for message ${ message_url } : \`${ url } \`` ,
204- ) ;
205- } else {
206- this . wheatley . critical_error ( e ) ;
170+ const delay_ms = base_delay_ms * Math . pow ( 2 , attempt ) ;
171+ await delay ( delay_ms ) ;
207172 }
208- return null ;
209173 }
174+ throw new Error ( "Unreachable" ) ;
210175 }
211176
212177 async virustotal_scan (
@@ -274,10 +239,24 @@ export default class AntiExecutable extends BotComponent {
274239 ) {
275240 await Promise . all (
276241 attachments . map ( async attachment => {
277- const buffer = await this . fetch_attachment ( attachment . url , message_url ) ;
278- if ( buffer !== null ) {
279- await this . virustotal_scan ( buffer , flag_message , author , original_message ) ;
242+ // download
243+ let file_buffer : Buffer ;
244+ try {
245+ file_buffer = await this . fetch_with_retry ( attachment . url ) ;
246+ } catch ( e ) {
247+ if ( e instanceof HTTPError ) {
248+ this . wheatley . warn (
249+ `HTTP Error ${ e . status_code } while fetching attachment for ` +
250+ `message ${ message_url } : \`${ attachment . url } \`` ,
251+ ) ;
252+ return ;
253+ } else {
254+ this . wheatley . critical_error ( e ) ;
255+ return ;
256+ }
280257 }
258+ // virustotal
259+ await this . virustotal_scan ( file_buffer , flag_message , author , original_message ) ;
281260 } ) ,
282261 ) ;
283262 }
@@ -314,14 +293,24 @@ export default class AntiExecutable extends BotComponent {
314293 const executables : Discord . Attachment [ ] = [ ] ;
315294 const archives : Discord . Attachment [ ] = [ ] ;
316295 for ( const [ _ , attachment ] of message . attachments ) {
317- const buffer = await this . fetch_attachment ( attachment . url , message . url , 512 ) ;
318- if ( buffer === null ) {
319- return ;
320- }
321- if ( this . looks_like_executable ( buffer ) ) {
322- executables . push ( attachment ) ;
323- } else if ( this . looks_like_archive ( buffer ) ) {
324- archives . push ( attachment ) ;
296+ try {
297+ const res = await this . fetch_with_retry ( attachment . url , 512 ) ;
298+ if ( this . looks_like_executable ( res ) ) {
299+ executables . push ( attachment ) ;
300+ } else if ( this . looks_like_archive ( res ) ) {
301+ archives . push ( attachment ) ;
302+ }
303+ } catch ( e ) {
304+ if ( e instanceof HTTPError ) {
305+ this . wheatley . warn (
306+ `HTTP Error ${ e . status_code } while fetching attachment for ` +
307+ `message ${ message . url } : \`${ attachment . url } \`` ,
308+ ) ;
309+ return ;
310+ } else {
311+ this . wheatley . critical_error ( e ) ;
312+ return ;
313+ }
325314 }
326315 }
327316 if ( executables . length > 0 ) {
0 commit comments