Return data in webhook response?

chaoscreater

Active member
Hi all,

Is it possible to have the Macrodroid webhook return data to the caller? For example, on my Windows PC I might do something like this in Powershell:

$url = 'https://trigger.macrodroid.com/xxxxxxxxxxxx-0135-41e0-8db3-3c47aba7725f/Phone_IP_Address'

$response = Invoke-RestMethod -Uri $url -Method GET -UseBasicParsing

And on my phone, the webhook will trigger a bunch of actions to run, where it gets the IP address assigned to my phone and then pass that IP address as a result back to the caller.

In the $response variable, the output would then look something like "192.168.1.140". I can then take that and do something else with it on my PC.

However, the actual scenario is that you just get "ok" in the response.

If this isn't possible, is there some sort of mechanism that allows me to do this?
 

Dimlos

Well-known member
Do you want to know the IP of your smartphone from a PC in the same LAN?

Powershell command
Get-NetNeighbor -LinkLayerAddress "XX-XX-XX-XX-XX-XX"

XX-XX-XX-XX-XX-XX is MAC Address
 

chaoscreater

Active member
Thanks, so that's basically ARP table lookup but targeted to the phone's MAC address.

I guess it works, but the prob is that my phone has MAC address randomizer turned on in the wifi setting. It's a native/built-in setting that you can find on later versions of Android (actually it's been around for a while).

I have it turned on for certain networks, for reasons I won't get into. So it'd be hard to get the MAC address since it's not static...
 

Dimlos

Well-known member
If it is a standard feature, random is just a name, meaning it uses an address other than the device's MAC address.
No matter how much you switch or reboot, the random MAC address remains the same, so you only need to search for two MAC addresses.
 

chaoscreater

Active member
I'm thinking of doing something like this, not sure if there's a better solution:

1. Powershell call MacroDroid webhook

2. MacroDroid webhook trigger will run some actions to get the IP address on the phone. Then, pass this value into another webhook service , which is free to use - https://webhook.link/xxxxxxxxxxxxxxxxxxxxxxxx

3. Powershell then retrieves the data from the same webhook service, but using slightly modified URL in order to get the data and also in JSON format - https://webhook.link/api/inspect/xxxxxxxxxxxxxxxxxxxxxxx

4. From here, Powershell can look at each element (i.e each message) that is sent to the webhook on https://webhoo.link..... and parse the data. The data would obviously contain the IP address.

Problem with the above is that the time is in GMT, which isn't a big deal as I can convert it to my local time. But the other problem is that the webhook is only valid for 7 days. Not sure what happens when it expires, whether it's just gonna wipe all the data but you get to re-use it again, or the webhook URL just doesn't work and you'll have to generate a new one.
 

chaoscreater

Active member
If it is a standard feature, random is just a name, meaning it uses an address other than the device's MAC address.
No matter how much you switch or reboot, the random MAC address remains the same, so you only need to search for two MAC addresses.
Not quite sure I understand what you mean.

On my own network, I've logged into my router and I can see the MAC address of my phone. The IP address is for example 192.168.0.140.

On my phone, if I then enable MAC randomization setting for my home Wifi network, then when I disconnect and reconnect Wifi, the IP address will change to e.g. 192.168.0.155.

If I now check my router, I can see that there's a new MAC address and the IP assigned is 192.168.0.155.

On my PC, if I then lookup the old MAC address of my phone, nothing is returned. Only the new MAC address returns data.

The IP address is not assigned to the old MAC, it's assigned to the new MAC. This makes sense, because you're randomizing the MAC address on the phone, i.e MAC spoofing.
 

Dimlos

Well-known member
If it is random, I would like to see a different address set each time, but as far as I know, the address set as a random MAC address is fixed.
 

Dimlos

Well-known member
Suppose it is this address.

device mac address > c1-b0-b2-70-e1-db
random mac address > f0-1c-e5-73-fc-32

Even if you switch the settings, the random mac address will always be f0-1c-e5-73-fc-32.
This means that only two types of mac addresses can be set.
 

chaoscreater

Active member
Hmm if that's the case then it's not a good randomization. I imagine that the random mac address is per wifi SSID that you connect to.

I'll have to test it out. On the one hand, it'll be simple if that's the case. On the other, it's not a true randomization and that might be an issue, but that's a separate problem. For example, in some airports you get free wifi only for about 30 mins and they restrict this based on your device's MAC address. If they always pick up the same MAC address even if it's randomized, then I'll only get 30 mins max lol. This is just an example though, could be other use cases where randomization is required.
 

mapriex

Active member
a device with a network controller has always a fixed mac adress, otherwise you aren't able communicate with each other. there are methods to prevent this like spoofing.
 

Endercraft

Moderator (& bug finder :D)
@chaoscreater I spent 4 hours (I should be asleep!) making a two way communication with webhooks.
In one request you can send the data and get some back.
The server is online 24/7.

Example request in JavaScript:
JavaScript:
fetch("https://vps-44c82946.vps.ovh.net/md/web/two-way/uuidhere-more-uuid-here-moreuuidhere/e?query=battery",{method:"GET", body:null}).then(res => res.json()).then((res) => {console.log(res)})
All info is sent in JSON.

It works like this:
You make a request to the server.
The server verifies the device id then if valid sends your request identical to your original request. The only addition is the response-id variable will be set (it will overwrite it if you try and set it manually).
Your device receives the webhook and does its things before sending back a response to the server.
Your request (the same that you first made) is answered with the response of the device or if it hasn't sent any within 30 second the response will be null.
In the attached macro you can see how it works.

Source code of the server:
JavaScript:
(() => {
    var pendingRequestsResponses = {}
    const headers = {
        'Content-Type': 'application/json'
    }

    function handleRequest(req, res, body) {
        let uuid = req.url.match("(?<=^\/md\/web\/two-way\/)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=\/.*$)");
        if (!uuid) {
            res.writeHead(400, headers);
            let resp = { error: { basic: "Invalid request", detailed: "The provided request is invalid (the id couldn't be found).", errcode: "err_invalid_uuid" } }
            res.write(JSON.stringify(resp));
            res.end();
            return;
        }
        uuid = uuid[0];
        fetch(`https://trigger.macrodroid.com/${uuid}/`).then(async response => {
            if (response.status == 404) {
                res.writeHead(404, headers);
                let resp = { error: { basic: "Invalid id", detailed: "The provided device id is invalid.", errcode: "err_invalid_uuid" } }
                res.write(JSON.stringify(resp));
                res.end();
                return;
            } else if (response.status == 200) {
                let rid = req.url.match("(?<=^\/md\/web\/two-way\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/).*$");
                if (!rid) rid = [""];
                rid = rid[0];
                let id = hash(Date.now().toString() + Math.random().toString());
                pendingRequestsResponses[id] = { "time": Date.now(), "response": null }
                pendingRequestsResponses[id]["promise"] = new Promise(resolve => {
                    pendingRequestsResponses[id]["resolve"] = resolve;
                })
                setTimeout(() => {
                    if (pendingRequestsResponses[id]) {
                        pendingRequestsResponses[id].resolve();
                        setTimeout(() => {
                            delete pendingRequestsResponses[id];
                        }, 1000)
                    }
                }, 30000)
                let thisorthat = "?";
                if (rid.includes("?")) thisorthat = "&";
                if (rid.match(/(\?|&)response-id=/)) rid = rid.replaceAll(/response-id=.*?(?=$|&)/g, "response-id=" + id);
                let method;
                if (body) { method = "POST" } else { method = "GET" };
                let r = await fetch(`https://trigger.macrodroid.com/${uuid}/${rid}${thisorthat}response-id=` + id, { method: method, body: body }).catch(() => {
                    res.writeHead(500, headers);
                    let resp = { error: { basic: "Request error", detailed: "The server encountered an error making the request.", errcode: "err_request_error" } }
                    res.write(JSON.stringify(resp));
                    res.end();
                    return;
                })
                if (r.status !== 200) {
                    res.writeHead(424, headers);
                    let restext = await res.text();
                    let resp = { error: { basic: "Request error", detailed: "The backend encountered an error making the request. " + restext, errcode: "err_backend_request_error" } }
                    res.write(JSON.stringify(resp));
                    return;
                } else {
                    res.writeHead(200, headers);
                    await pendingRequestsResponses[id]["promise"];
                    let resp = { response: pendingRequestsResponses[id]["response"] };
                    res.write(JSON.stringify(resp));
                    res.end();
                }
            } else {
                res.writeHead(424, headers);
                let restext = await res.text();
                let resp = { error: { basic: "Unknown error", detailed: "The backend encountered an error verifying the device id. " + restext, errcode: "err_device_verification_backend_error" } }
                res.write(JSON.stringify(resp));
                res.end();
                return;
            }
        }).catch((e) => {
            console.log(e);
            res.writeHead(500, headers);
            let resp = { error: { basic: "Verification error", detailed: "The server encountered an error verifying the device id.", errcode: "err_device_verification_error" } }
            res.write(JSON.stringify(resp));
            res.end();
        }
        )
    }

    app.get("/md/web/two-way/*", async (req, res) => {
        handleRequest(req, res)
    })

    app.post("/md/web/two-way/*", async (req, res) => {
        let body = [];
        req
            .on('data', chunk => { body.push(chunk) })
            .on('end', () => {
                body = Buffer.concat(body).toString();
                handleRequest(req, res, body)
            })
    })

    app.post("/md/web/two-way-response/*", async (req, res) => {
        let id = req.query["response-id"]
        if (pendingRequestsResponses[id]) {
            if (pendingRequestsResponses[id]["response"]) {
                res.writeHead(409, headers)
                let resp = { error: { basic: "Response conflict", detailed: "The response id already has a response assigned to it.", errcode: "err_response_conflict" } }
                res.write(JSON.stringify(resp))
                res.end()
            }
            let body = [];
            req
                .on('data', chunk => { body.push(chunk) })
                .on('end', () => {
                    body = Buffer.concat(body).toString();
                    // at this point, `body` has the entire request body stored in it as a string
                    if (pendingRequestsResponses[id]) {
                        res.writeHead(200, headers)
                        let resp = { response: "Everything went correctly. Or did it?", rescode: "success" }
                        res.write(JSON.stringify(resp))
                        res.end()
                        pendingRequestsResponses[id]["response"] = body
                        pendingRequestsResponses[id].resolve()
                    } else {
                        res.writeHead(410, headers)
                        let resp = { error: { basic: "Too late!", detailed: "The provided response id is valid but the original request was finished before this request could finish.", errcode: "err_response_send_timeout" } }
                        res.write(JSON.stringify(resp))
                        res.end()
                    }
                });

        } else {
            res.writeHead(404, headers)
            let resp = { error: { basic: "Invalid response id", detailed: "The provided response id is invalid or expired.", errcode: "err_invalid_response_id" } }
            res.write(JSON.stringify(resp))
            res.end()
        }
    })
}
)()

It runs in a kind of sandbox so its variables don't interfere with other things in the server (it serves other functions hence the 401 on every other url).
 

Attachments

  • E_handler.macro
    4.1 KB · Views: 6

chaoscreater

Active member
@Dimlos @mapriex

Looks like the method that @Dimlos suggested isn't that reliable.

$Phone_IP = Get-NetNeighbor -LinkLayerAddress xx-xx-xx-xx-xx-xx
scrcpy --tcpip=$($Phone_IP.IPAddress):5555

This works when my PC has the phone's MAC address in the ARP table. If I know the phone's IP address (e.g. 192.168.0.140) and I ping it from my PC, then the ARP table gets populated.

The problem is that sometimes, the phone's MAC address disappears from the ARP table. Even though the IP hasn't changed, running the Get-NetNeighbor command will fail. The MAC address of the phone hasn't changed either.
 

mapriex

Active member
@Dimlos @mapriex

Looks like the method that @Dimlos suggested isn't that reliable.



This works when my PC has the phone's MAC address in the ARP table. If I know the phone's IP address (e.g. 192.168.0.140) and I ping it from my PC, then the ARP table gets populated.

The problem is that sometimes, the phone's MAC address disappears from the ARP table. Even though the IP hasn't changed, running the Get-NetNeighbor command will fail. The MAC address of the phone hasn't changed either.
i guess its a battery save function of a mobile android device.

maybe you can provide some double pings and delays to reactivate it with the network, but i am not sure.
 

chaoscreater

Active member
i guess its a battery save function of a mobile android device.

maybe you can provide some double pings and delays to reactivate it with the network, but i am not sure.
Could you elaborate further please?

Ping would require you to know the IP address in the first place. If I knew the IP address, I wouldn't need to retrieve it via MAC address lookup.

As for battery save function, not sure what effect it would have caused. The phone is still using wifi in the background and even running some uploads to my cloud drive.
 

mapriex

Active member
my mobile devices connect and disconnect from my local network. if you make a time related ping request it should ask the device, if its reachable till it finds the mobile device. then continue.
maybe make a max limit of 5 minutes, where you ask every 30 seconds the ip adress.

do you have static ip adress?
 

chaoscreater

Active member
That's not ideal though, because you're allocating resources (albeit not much) to keeping that "connection" alive. What if my laptop is powered off or hibernated? What if I take my laptop to work and during the commute, there's no wifi to connect to on either devices? There's a lot of situations where they can't be covered by a interval ping.

As for static IP address, same thing as above. I do have it in my local network at home, in which case I don't even need workarounds to get the IP address, because I simply know it by heart. But in other networks, say in the office, you don't get assigned static IPs to a personal device.

This is why I think the most reliable way is for both devices (PC and phone) to talk to a middleman server (a proxy if you will), where in my case it'd be a webhook server that takes the data (wifi IP address) pushed by the phone and then the PC can take that and use it.
 

mapriex

Active member
talk to a middleman server (a proxy if you will),
how do you power the proxy? its the same source that you need to power the pc.

anyway i know its not ideal, but a workaround, if you want a reliable communication.

your request was talking from the pc to the mobile device with changing macs. i provided a reliable solution for this. if your mobile device is turned off, then the "outtime"-time is there to cancel the communication. its a simple p2p communication that i suggested. if its not that needed, then you might look around for another solutions i am not aware of. in needs of a proxy server: i don't think its worth, but of course it depends on you what is important.
 

chaoscreater

Active member
how do you power the proxy? its the same source that you need to power the pc.

anyway i know its not ideal, but a workaround, if you want a reliable communication.

your request was talking from the pc to the mobile device with changing macs. i provided a reliable solution for this. if your mobile device is turned off, then the "outtime"-time is there to cancel the communication. its a simple p2p communication that i suggested. if its not that needed, then you might look around for another solutions i am not aware of. in needs of a proxy server: i don't think its worth, but of course it depends on you what is important.
I'm not hosting the proxy myself. I discussed this briefly in an earlier post if you're interested in that solution:
https://www.macrodroidforum.com/index.php?threads/return-data-in-webhook-response.6832/#post-40756

Pinging by IP is not going to work because it assumes the IP is static. If you're in an office environment, you'd likely be assigned IP via DHCP. At that point, it doesn't matter whether the phone is on or not.

There is no DNS hostname associated with the device either, because unlike a desktop OS, it doesn't support DNS auto-registration. This means that you can't ping using something static (like a hostname) reliably.

By using a middleman server that is hosted by someone, you're allowing both PC and mobile to talk to it, without passing in sensitive data of course. It doesn't matter what network the mobile device is on, it doesn't matter whether it has a dynamic or static IP, it'll just pass its IP information to the proxy, which then can be fetched by the PC via a GET request.
 
Top