Search code examples
powershelldiscordwebhooksinvoke-webrequest

Uploading a text file to discord using webhook


I looked through and tested a few examples I saw online with no success. From what I understand it should look something like the code below:

$hookUrl = 'https://discord.com/api/webhooks/XXXXXXXXXXXXXXXXXXXX'

$Body = @{
  'username' = $env:username
  'content' = "this is a test"
  "file=@C:\Users\User\Desktop\test.txt"
}



Invoke-WebRequest -uri $hookUrl -Method POST -Body $Body -Headers @{'Content-Type' = 'application/json'}

ERRORS

Invoke-WebRequest : {"code": 50109, "message": "The request body contains invalid JSON."}
At line:11 char:1
+ Invoke-WebRequest -uri $hookUrl -Method POST -Body $Body -Headers @{' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

I have seen a few extensively long methods to achieve this in the documentation, however if you see below I will post a one liner that accomplishes what I want using CMD. Is it really this simple in CMD but in powershell it takes 15+ lines?

curl -F "payload_json={\"username\": \"jakoby\", \"content\": \"download me\"}" -F "file=@\"C:\Users\User\Desktop\newUser.txt\"" WEB-HOOK

Solution

  • Update:

    • The answer below (next section) addresses the original form of your question.

    • It later emerged that you're looking for the PowerShell equivalent of a curl command line that uses a multipart/form-data submission to submit both JSON and upload a local file.

      • Example 6 in the Invoke-WebRequest help topic shows you how to do that, but it is more verbose than the curl command.

      • The simplest solution may therefore be to simply call your curl command from PowerShell, but be sure to use curl.exe to unambiguously target the external executable, not the curl alias for Invoke-WebRequest that is built into Windows PowerShell (it has been removed in PowerShell (Core) 7+).

        # In PowerShell 7.3+, the "\" chars. are *no longer necessary*.
        curl.exe -F "payload_json={\`"username\`": \`"jakoby\`", \`"content\`": \`"download me\`"}" -F "file=@\`"C:\Users\User\Desktop\newUser.txt\`"" WEB-HOOK
        
        • Note the unfortunate need to escape the embedded " twice:
          • Once, with `, to satisfy PowerShell's syntax requirements for double-quoted strings (as expected).
            • You could obviate the need for this if you used '...' for the overall quoting, but that would preclude embedding variable values directly in the string.
          • [Update: No longer necessary in v7.3+ - see this answer] Unexpectedly again, with \, to work around a long-standing bug with respect to passing arguments containing verbatim " chars. to external programs.

    Since the target web service expects JSON, you must convert your $Body hashtable to JSON before passing it to Invoke-WebRequest's -Body parameter, which you can do with ConvertTo-Json:

    Invoke-WebRequest -uri $hookUrl -Method POST -Body (ConvertTo-Json $Body) -Headers @{'Content-Type' = 'application/json'}
    

    The obligatory general caveat: with more deeply nested objects, you may need to pass a -Depth argument to ConvertTo-Json to prevent accidental truncation of data - see this post.


    It seems that you also want to upload a local file:

    • Since the web service has no access to your local file system, passing a local file path as part of your JSON cannot work - the local file's content must be uploaded.

    • The Invoke-WebRequest docs only discuss uploading local files in the context of multipart/form-data submissions - see example 6, for instance.