i'm trying to write my own custom webdriver interface to control firefox through chrome devtools protocol (cdp). i launch firefox using firefox-esr --remote-debugging-port 0 and then it creates a websocket that i connect to using python:
async def main_client():
async with websockets.connect("ws://localhost:34805/devtools/browser/f67014fd-9397-478d-a11e-66c189704ab0") as client_connection:
while True:
message = input("type in a command: ")
await client_connection.send(message)
asyncio.run(main_client())
then i send a message in json format
{
"command":"Runtime.evaluate",
"parameters":{"expression": "console.log(\"this is a message\")"}
}
the problem is that when i send the message nothing happens on the receiving end. does anyone know how to send messages over websocket to firefox?
thanks
so i did a bit more research and it turns out that to be able to interact with firefox cdp after starting using ./firefox --remote-debugging-port 12345 you need to make a get request to localhost:12345/json/list. here you will find the list of websocket targets you can connect to. by default you have a top most browser target that doesn't have any tab elements to interact with and then you have tab targets that you can interact with. here is any example of a json list when starting firefox cdp:
[
{
"description": "",
"devtoolsFrontendUrl": null,
"faviconUrl": "",
"id": "ef9b04c6-409f-4fe9-bea9-c50979049820",
"type": "page",
"url": "about:blank",
"webSocketDebuggerUrl": "ws://127.0.0.1:12345/devtools/page/ef9b04c6-409f-4fe9-bea9-c50979049820"
},
{
"description": "Main process target",
"devtoolsFrontendUrl": "",
"faviconUrl": "",
"id": "c726e615-36cc-4a73-a48a-a75cc0fa941e",
"title": "Main process target",
"type": "browser",
"url": "",
"webSocketDebuggerUrl": "ws://127.0.0.1:12345/devtools/browser/c726e615-36cc-4a73-a48a-a75cc0fa941e"
}
]
after getting the json response with the json list then you can connect to one of the websockets using python and then you can send messages through websocket.
not all targets support all cdp commands. also some cdp commands require a response but some don't, although if the command was unsuccessful, you will get a response with the error message.
you can find a list of all cdp commands at https://chromedevtools.github.io/devtools-protocol/ and you can find all the supported cdp commands by your firefox version at http://localhost:12345/json/protocol after launching firefox with cdp.
i still haven't figured out how to run Runtime.evaluate because you need to specify a contextId (it says it's optional but when sending commands through websocket it is required) and i don't know where to get the current context id from. if anyone finds out let me know.
Related
I'm attempting to authenticate to a website to automate some device configuration.
There is no official API so I'm using "WebSpy" in my browser to watch what URLs are targeted and the payloads being sent.
I'm unable to get initial authentication working with a python post request.
The target url is https://xxxxxx.xxx/authenticate.
The payload I see when logging in from a web browser is.
{ "client_id": xxxxxx,
"username": <plainText username>,
"password": <plainText password>,
"realm": "xxxxx",
"credential_type": "http://auth0.com/oauth/grant-type/password-realm"}
If I replicate all this in a python requests.POST I get back
{ "error": "invalid request",
"error_description": "Unknown client."}
I should mention the "client_id" I'm sending in my python post is just copied from what I see coming from the browser.
I imagine that client ID should be dynamically generated somehow but I don't see where it's coming from.
I should also mention I see some reference to a \callback URL happening after login within the web browser so I'm guessing that is how/when the auth token is being offered.
Can anyone point me in the right direction on all this?
Thank you in advance.
I would like to read chrome's js console using Python3 without any webdriver such as selenium (bot detection and stuff).
I've tried Chrome DevTools Protocol python libraries such as chromewhip, pychrome and PyChromeDevTools, but I'm unable to read any data from the console.
I want to read Runtime.consoleAPICalled or Log.entryAdded, but I don't know how to implement these callbacks as the documentation for these libraries doesn't specify any of that. Also there are no examples to be found either.
Does anyone know how to properly access these events or some other library which provides it?
#kundapanda could you at least post a snippet of the code that worked for you.
I want to read Runtime.consoleAPICalled or Log.entryAdded, but I don't
know how to implement these callbacks
The following assumes (per your question phrasing) that you're able to send and receive debug protocol messages on the stream that's open to the web debugger endpoint.
After you send debug protocol messages Runtime.enable and Log.enable messages, the Runtime.consoleAPICalled and Log.entryAdded "events" you are looking for are represented by messages you receive on the same debugging channel.
You may need to match the console event messages with the execution context (seen in the Runtime.enable response) by examining the executionContextId field in the received event messages. The log events are not associated with any single execution context. All of these "event" messages will have Id=0, which helps to recognize they're "event" messages and not response messages.
Here are a couple of sample messages received from Chrome (formatted as JSON with arbitrary field order):
Console API event message:
{
"method": "Runtime.consoleAPICalled",
"params":
{
"type": "warning",
"args": [
{ "type": "string",
"value": "Google Maps JavaScript API warning: NoApiKeys https://developers.google.com/maps/documentation/javascript/error-messages#no-api-keys"
}],
"executionContextId": 1,
"timestamp": 1618949706735.553,
"stackTrace":
{
"callFrames": [
{
"functionName": "TA.j",
"scriptId": "206",
"url": "https://maps.googleapis.com/maps-api-v3/api/js/44/10/util.js",
"lineNumber": 228,
"columnNumber": 26
} ]
}
}
},
"id": 0
}
Log event message:
{
"method":"Log.entryAdded",
"params":
{
"entry":
{
"source":"javascript",
"level":"warning",
"text":"The deviceorientation events are blocked by permissions policy. See https://github.com/w3c/webappsec-permissions-policy/blob/master/features.md#sensor-features",
"timestamp":1.6189509536801208e+12
}
},
"id": 0
}
I have a slack /Slash command that generates the app file link. I want to send that link to a user as a button or link but it has to be prefixed with:
itms-services://?action=download-manifest&url={download_link}
Currently slack is not recognizing this as a valid link but it's mandatory by iOS because of permission issues.
Basically I want to mimic the download button on web on slack so user is not required to visit the website.
On Web this link itms-services://?action=download-manifest&url={download_link} works fine and user is asked to authorize the download.
test_attachment = [
{
"color": "#CC0000",
"actions": [
{
"type": "button",
"text": ":red_circle: Download Link:",
"url": "itms-services://?action=download-manifest&url={download_link}"
}
]
}
]
slack_client.api_call("chat.postMessage", channel=channel_id, text=text, attachments=json.dumps(test_attachment))
Another example:
<a href="itms-services://?action=download-manifest&url=http://loqi.me/app/Geoloqi.plist">
Download Geoloqi
</a>
This will work on Web and how to make it work on slack messages.
An itms-services link is not recognize as a valid URL by slack parser because its address (the thing we usually found just after ://) is empty.
A quick fix is to fill it with a dummy address for example 0.0.0.0.
Here is a block kit that works :
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Try to install me:"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Install",
"emoji": true
},
"url": "itms-services://0.0.0.0?action=download-manifest&url={download_link}"
}
}
]
}
and it looks like :
I had a similar problem in one of my apps where I wanted to provide a download button for a dynamically generated image file.
As far as I know there is no switch or option in Slack that allows you to do this, so your only option is to use a helper web app.
The approach would be:
User clicks a link button on Slack that opens your helper web app in the browser
Helper web app performs the actual download (no further user interaction required)
My guess is the user will need to authenticate in the browser anyways, so this should not create any inconvenience for the user. But of course you would need to provide the additional web helper app.
I am trying to build a Slack bot in Python. I want my bot to send a message with interactive buttons on it, and then based on which the user clicks on, run a function in Python. I do not seem to find how to do this.
My code now look like this:
message = "message"
attachments = [{"text": "message",
"attachment_type": "default",
"actions": [
{
"name": "list",
"text": "message",
"type": "select",
"options": [
{
"name": "1",
"text": "1",
"type": "button",
"value": "1"
},
{
"name": "1",
"text": "1",
"type": "button",
"value": "2"
}
]}]}]
sc.api_call("chat.postMessage",
channel=channel,
text=message,
attachments=attachments)
So that gives me a message with two buttons. I, however, want to run a function based on the answer the user gives.
So say that if they click 1, function1() runs and when they click 2, function2() runs.
The Slack api documentation is quite confusing about how to do this, and the "listener" they provide rtm_read() does not pick on the user clicking on one of the buttons.
So if anyone could help me with this, it would be much appreciated.
When you click a button in a slack conversation it's basically applying a callback. The callback is sent somewhere that you define in the App's setting, then THAT service decides what to do next with the information that's given.
First you need to create a new Slack App.
After it's created click on the App to go to its Basic Information page.
From there, on the left side under Features find "Interactive Components".
Register the two URLs that will receive the POST data from clicking on a button.
Interpret the data and proceed :)
From the Slack documentation you can find their walkthrough here.
You're going to need a running web server, something simple in Flask will work just fine.
from flask import Flask, request
app = Flask('SlackReceiver')
#app.route('/slack/message', methods=['POST'])
def incoming_slack_message():
req = request.get_json()
# .. do something with the req ..
return 'action successful'
#app.route('/slack/options', methods=['POST', 'OPTIONS'])
def incoming_slack_options():
# .. idk ..
return 'ok'
if __name__ == '__main__':
app.run('0.0.0.0', 8088, debug=False)
...
Lastly, according to the docs you need to host this application on a web server with an HTTPS valid certificate configured. Setting up a server is beyond the scope of this question, the easiest way to get free (valid) HTTPS certs is with Let's Encrypt and certbot.
I am trying to use the mirror-api-python-cli command line interface to send timeline cards from Raspberry Pi to Google Glass. I am able to complete the first step which uses the code in get-credentials.py to connect to Google with my application client-id and secret. This code prompts to go to an authorisation URL to obtain a code and upon entering the code it does go on to correctly populate a credentials file with authentication information including access_token and refresh_token.
I am then running the code in the second file, send-to-glass.py, to pass a 'hello world' message to the Google Glass timeline. I am receiving no error message and yet nothing is received by the Glass.
I have created a separate web client for the application using the Google playground and that is able to send cards to the timeline so I know there is no problem on the Google application side.
I have also done a print of the insert_timeline_item call and I think that might be giving a clue as to where the issue lies. The last name value pair in the json response is selfLink which has a url of https://www.googleapis.com/mirror/v1/timeline/xxxxxx where xxxxxx is the id value. If I follow this URL then I get the following:
{ "error": { "errors": [{ "domain": "global", "reason": "required", "message": "Login Required", "locationType": "header", "location": "Authorization" }], "code": 401, "message": "Login Required" }}
---------- UPDATE ----------
I used CURL with a fresh access token, as suggested by Jenny. I must admit I am not familiar with CURL and so it took a few tries to get it to work. First the address was not resolving, so I added www in front of googleapis, then it was complaining that SSL was required so I added https in front, then it complained about the certificate so I added the -k parameter and finally it was trying to resolve Bearer and my access tokens as addresses so I used double quotes instead of single quotes. Please feel free to chuckle at the noob here but possibly this may help someone in the future!
My final curl command looked like this:
{ curl -H "Authorization: Bearer MY-ACCESS-TOKEN" https://www.googleapis.com/mirror/v1/timeline -k }
The result came back with a whole load of json, with what looked like all of the timeline cards I tried to send from the Raspberry Pi. I won't list all the entries, but the top of the json looks like this:
{"kind": "mirror#timeline", "nextPageToken": "LONG-STRING-OF-CHARACTERS",
"items": [
{
"kind": "mirror#timelineItem",
"id": "ITEM-ID",
"selfLink": "https://www.googleapis.com/mirror/v1/timeline/ITEM-ID",
"created": "2014-04-19T01:40:40.597Z",
"updated": "2014-04-19T01:40:40.597Z",
"etag": "1397871640597",
"text": "Hello World",
"notification": {
"level": "DEFAULT"
}
},
{ ... }
So it would appear that somehow the cards are getting to my timeline and yet they are not being delivered to the Glass. As a reminder, using a web client in the playground against the same project I was able to see cards come through to Glass.
So this is a little embarrassing, I have discovered exactly what the problem was. This whole exercise is related to a science fair project my son is doing. He had been programming the Raspberry Pi through WebIDE logged on through his Google Account. I had set up the project to access Glass through my Google account in a separate Chrome browser. Each time I ran get-credentials.py on the Raspberry Pi and it directed me to copy the URL into the browser for approval, I was doing so in my son's browser with his Google account. The credentials were being saved correctly and cards were populating a timeline correctly, but all against my son's Google account and he has no Glass!
I re-ran get-credentials.py, ran the URL in a browser session associated with my own account. Copied the code back to the Pi and now send-to-glass.py works perfectly. My son thinks I'm an idiot and is getting plenty of laughs out of it, but I am pleased to report everything is now working.