Categories
PowerShell Quick Tips

Creating a Discord Webhook in a few lines of PowerShell

Discord Web Hooks are easier than you’d think. It uses a regular `HTTP` web request .

Step1: Creating your webhook url

Go to: Server Settings -> Integrations -> View Webhooks
Then click create webhook.

Choose a name, the channel, then copy webhook url

Step2: Invoke-RestMethod

$webhookUri = 'https://discord.com/api/YourWebhookUrlHere'

$Body = @{
  'username' = 'Nomad'
  'content' = 'https://memory-alpha.fandom.com/wiki/Nomad'
}
Invoke-RestMethod -Uri $webhookUri -Method 'post' -Body $Body

Success!

Docs

There are optional webhook arguments to customize the output: https://discord.com/developers/docs/resources/webhook#execute-webhook

Comparison with curl

I tried making curl easier to read by splitting content, and pipe so that you don’t have to manually escape Json

$Json = @{ 'username' = 'Nomad'; 'content' = 'https://memory-alpha.fandom.com/wiki/Nomad' } | ConvertTo-Json -Compress

$Json | curl -X POST -H 'Content-Type: application/json' -d '@-' $webhookUri

Categories
Power BI Power Query

Web.Contents: Using Dynamic and Duplicate key names in a Query

Power BI Discord asked the question:
> How do you use duplicate keys, and dynamic urls with <a href="https://docs.microsoft.com/en-us/powerquery-m/web-contents">Web.Contents</a> ?
(They were using a web API that required duplicate keys)
After I wrote this, Chris Webb found an easier solution.

Requirements

  1. You can’t use a regular record because keys must be distinct. Query = [ Key1 = 1, Key1 = 10] will throw an error.
  2. You can’t put the **dynamic url** in the first argument Web.Contents or else refreshes can break

I built a RelativePath by Uri-escaping a list.

Building RelativePath by Uri-escaping a list

The final query will request the url: https://www.google.com/search?q=dog&q=cat
This function generates the query string q=dog&q=cat

This Input"q", { "dog", "cat" }
Will Returnq=dog&q=cat
let
    QueryStr_UsingDuplicateKeys = (key as text, values as list) as text =>
    // values are the 'value' of 'key'-> 'value' pairs
        let
            escapedList = List.Transform(
                values,
                each 
                    key & "=" & Uri.EscapeDataString( Text.From(_) )
            ),    
            joinedArgs = Text.Combine(escapedList, "&")
        in
            joinedArgs
in
    QueryStr_UsingDuplicateKeys

Now you can use Web.Contents as normal.

let
    BaseUrl = "https://www.google.com",
    queryStr = QueryStr_UsingDuplicateKeys(
        "q", {"dog", "cat"}
    ),
    Options = [
        RelativePath = "/search?" & queryStr,
        Headers = [ Accept="application/json" ]
    ],
    response_binary = Web.Contents(BaseUrl, Options)
in
    response_binary

Note: BaseUrl is for the static part of the url. Everything else should be in options[RelativePath] or options[Query] See docs: Web.Contents for details.

Easier Solution

First try Chris’s method where you use Query‘s Key-Value pairs a list
Results may vary. If it does not work, you can try this method.
I have not seen Query publicly documented.

Query = [
    q = {"dog", "cat"}
]

Tips from Chris Webb

Using Optional Parameters with Query

Chris has a tip to optionally use null query parameters.
If set to an empty list, {} — the request drops the parameter.
Web.Contents(
    "http://jsonplaceholder.typicode.com/comments",
    [Query = [postId = {}] ]
)

Which results in the url: <a href="http://jsonplaceholder.typicode.com/comments">http://jsonplaceholder.typicode.com/comments</a>
This is a good place to use Power Query‘s null coalesce operator (Which isn’t in the official docs)

Query = [postId = myPostId ?? {}]

Capturing HTTP Requests without Fiddler

The Query Editor is an alternate to using Fiddler to capture web requests.