discord-php / DiscordPHP

An API to interact with the popular messaging app Discord
MIT License
992 stars 236 forks source link

Interactions message content cannot be null #1239

Closed ellisonpatterson closed 2 months ago

ellisonpatterson commented 3 months ago

Environment

Describe the bug When responding to an interaction, if you only send a file without setting any content, you get this error:

[2024-07-01 12:07:29] WARNING: REQ POST interactions/xxx/xxx/callback failed: Discord\Http\Exceptions\BadRequestException: Bad Request - {
    "message": "Invalid Form Body",
    "code": 50035,
    "errors": {
        "data": {
            "_errors": [
                {
                    "code": "MODEL_TYPE_CONVERT",
                    "message": "Expected an object\/dictionary."
                }
            ]
        }
    }
} in /home/discord/cliapps/vendor/discord-php/http/src/Discord/Http.php:512

If I just do setContent('') in the same message, the interaction gets a response without any errors.

Now, if I send that same file to a channel I don't have to set the content to an empty string, it just works as intended.

This was not always the case and it seems to have been introduced by a commit in the library, or something on Discord's end.

To Reproduce Steps to reproduce the behavior:

SUCCESS: Sending a file to a channel:

$discord->once('init', function($discord) {
    $file = await(fileSystem()->detect(ABSPATH . '/storage/videos/xxx.mp4'));
    $fileContents = await($file->getContents());

    $discord->getChannel(xxx)->sendMessage(
        \Discord\Builders\MessageBuilder::new()
            ->addFileFromContent('xxx.mp4', $fileContents)
    );
});

FAILURE: Responding to an interaction without setting content:

async(function ($interaction) {
    $file = await(fileSystem()->detect(ABSPATH . '/storage/videos/xxx.mp4'));
    $fileContents = await($file->getContents());

    return $interaction->respondWithMessage(
        MessageBuilder::new()
            ->addFileFromContent('xxx.mp4', $fileContents)
    );
})($interaction);

SUCCESS: Responding to an interaction by setting content to an empty string:

async(function ($interaction) {
    $file = await(fileSystem()->detect(ABSPATH . '/storage/videos/xxx.mp4'));
    $fileContents = await($file->getContents());

    return $interaction->respondWithMessage(
        MessageBuilder::new()
            ->setContent('')
            ->addFileFromContent('xxx.mp4', $fileContents)
    );
})($interaction);

Full stacktrace:

[2024-07-01 12:32:12] DEBUG: BUCKET postinteractions/:interaction_id/:interaction_token/callback queued REQ POST interactions/xxx/xxx/callback [] []
[2024-07-01 12:32:12] WARNING: REQ POST interactions/xxx/xxx/callback failed: Discord\Http\Exceptions\BadRequestException: Bad Request - {
    "message": "Invalid Form Body",
    "code": 50035,
    "errors": {
        "data": {
            "_errors": [
                {
                    "code": "MODEL_TYPE_CONVERT",
                    "message": "Expected an object\/dictionary."
                }
            ]
        }
    }
} in /home/discord/cliapps/vendor/discord-php/http/src/Discord/Http.php:512
Stack trace:
#0 /home/discord/cliapps/vendor/discord-php/http/src/Discord/Http.php(398): Discord\Http\Http->handleError()
#1 /home/discord/cliapps/vendor/react/promise/src/FulfilledPromise.php(42): Discord\Http\Http->Discord\Http\{closure}()
#2 /home/discord/cliapps/vendor/react/promise/src/Promise.php(66): React\Promise\FulfilledPromise->done()
#3 /home/discord/cliapps/vendor/react/promise/src/Promise.php(168): React\Promise\Promise::React\Promise\{closure}()
#4 /home/discord/cliapps/vendor/react/promise/src/Promise.php(231): React\Promise\Promise->settle()
#5 /home/discord/cliapps/vendor/react/promise/src/Deferred.php(36): React\Promise\Promise::React\Promise\{closure}()
#6 /home/discord/cliapps/vendor/react/http/src/Io/Transaction.php(90): React\Promise\Deferred->resolve()
#7 /home/discord/cliapps/vendor/react/promise/src/FulfilledPromise.php(28): React\Http\Io\Transaction->React\Http\Io\{closure}()
#8 /home/discord/cliapps/vendor/react/promise/src/Promise.php(134): React\Promise\FulfilledPromise->then()
#9 /home/discord/cliapps/vendor/react/promise/src/Promise.php(168): React\Promise\Promise::React\Promise\{closure}()
#10 /home/discord/cliapps/vendor/react/promise/src/Promise.php(231): React\Promise\Promise->settle()
#11 /home/discord/cliapps/vendor/react/promise/src/FulfilledPromise.php(42): React\Promise\Promise::React\Promise\{closure}()
#12 /home/discord/cliapps/vendor/react/promise/src/Promise.php(135): React\Promise\FulfilledPromise->done()
#13 /home/discord/cliapps/vendor/react/promise/src/Promise.php(168): React\Promise\Promise::React\Promise\{closure}()
#14 /home/discord/cliapps/vendor/react/promise/src/Promise.php(231): React\Promise\Promise->settle()
#15 /home/discord/cliapps/vendor/react/promise/src/FulfilledPromise.php(42): React\Promise\Promise::React\Promise\{closure}()
#16 /home/discord/cliapps/vendor/react/promise/src/Promise.php(66): React\Promise\FulfilledPromise->done()
#17 /home/discord/cliapps/vendor/react/promise/src/Promise.php(168): React\Promise\Promise::React\Promise\{closure}()
#18 /home/discord/cliapps/vendor/react/promise/src/Promise.php(231): React\Promise\Promise->settle()
#19 /home/discord/cliapps/vendor/react/http/src/Io/Transaction.php(193): React\Promise\Promise::React\Promise\{closure}()
#20 /home/discord/cliapps/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\Transaction->React\Http\Io\{closure}()
#21 /home/discord/cliapps/vendor/react/http/src/Io/ReadableBodyStream.php(50): Evenement\EventEmitter->emit()
#22 /home/discord/cliapps/vendor/react/http/src/Io/ReadableBodyStream.php(151): React\Http\Io\ReadableBodyStream->close()
#23 /home/discord/cliapps/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ReadableBodyStream->handleEnd()
#24 /home/discord/cliapps/vendor/react/http/src/Io/ChunkedDecoder.php(150): Evenement\EventEmitter->emit()
#25 /home/discord/cliapps/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ChunkedDecoder->handleData()
#26 /home/discord/cliapps/vendor/react/http/src/Io/CloseProtectionStream.php(96): Evenement\EventEmitter->emit()
#27 /home/discord/cliapps/vendor/react/http/src/Io/ClientRequestStream.php(228): React\Http\Io\CloseProtectionStream->handleData()
#28 /home/discord/cliapps/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ClientRequestStream->handleData()
#29 /home/discord/cliapps/vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit()
#30 /home/discord/cliapps/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Stream\Util::React\Stream\{closure}()
#31 /home/discord/cliapps/vendor/react/stream/src/DuplexResourceStream.php(209): Evenement\EventEmitter->emit()
#32 /home/discord/cliapps/vendor/react/event-loop/src/ExtUvLoop.php(310): React\Stream\DuplexResourceStream->handleData()
#33 [internal function]: React\EventLoop\ExtUvLoop->React\EventLoop\{closure}()
#34 /home/discord/cliapps/vendor/react/event-loop/src/ExtUvLoop.php(232): uv_run()
#35 /home/discord/cliapps/development.php(54): React\EventLoop\ExtUvLoop->run()
#36 {main} [] []
[2024-07-01 12:32:12] DEBUG: http not checking {"waiting":0,"empty":true} []
SQKo commented 2 months ago

Thanks for reporting, looks like we missed the case when only file is send then the payload should be null.