Skip to content

Commit 637147d

Browse files
committed
Initial commit
0 parents  commit 637147d

File tree

10 files changed

+250
-0
lines changed

10 files changed

+250
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
**/node_modules
2+
**/build
3+
.vscode
4+
__pycache__

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# cookie-sync
2+
3+
## Description
4+
5+
## About
6+
7+
This is a simple extension that allows you to sync cookies between Chrome and Python using the [websocket](https://en.wikipedia.org/wiki/WebSocket) protocol.
8+
9+
## Requirements
10+
11+
- Python >= 3.7
12+
- [aiohttp](https://docs.aiohttp.org/en/stable)
13+
14+
## Usage
15+
16+
1. `pip install cookie-sync`
17+
18+
2. Download the and unarchive the extension from [releases](https://github.com/sunney-x/cookie-sync/releases)
19+
20+
3. Set `host_permissions` for the target website in `manifest.json`
21+
22+
4. Load the extension in Chrome
23+
- Go to `chrome://extensions`
24+
- Click `Load unpacked` and locate the extension folder
25+
26+
5. Quickstart example
27+
```python
28+
import aiohttp
29+
from cookie_sync import run_server
30+
31+
session = aiohttp.ClientSession()
32+
33+
run_server(
34+
session=session,
35+
domain='https://google.com'
36+
)
37+
```
38+
39+
## Documentation
40+
41+
### **run_server** configuration options
42+
43+
```py
44+
session: aiohttp.ClientSession
45+
- 'The session to keep cookies synced on'
46+
47+
domain: str
48+
- 'The domain to sync cookies for'
49+
50+
cookies: Optional[list] # Default []
51+
- 'List of cookies to sync, if empty, all cookies will be synced'
52+
53+
wait_for_cookies: Optional[bool] # Default True
54+
- 'If `False`, will not wait for the cookies to be synced'
55+
56+
timeout: Optional[int] # Default 30
57+
- 'Timeout in seconds to wait for `wait_for_cookies`'
58+
```

cookie_sync/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .server import run_server

cookie_sync/server.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import time
2+
from aiohttp import web
3+
from typing import Dict, List
4+
import aiohttp
5+
import json
6+
import threading
7+
import asyncio
8+
9+
10+
def _run_server(
11+
session: aiohttp.ClientSession,
12+
cookie_names: List[str],
13+
domain: str,
14+
):
15+
async def websocket_handler(request):
16+
ws = web.WebSocketResponse()
17+
await ws.prepare(request)
18+
19+
await ws.send_json({
20+
'action': 'get',
21+
'cookies': cookie_names,
22+
'domain': domain,
23+
})
24+
25+
async for msg in ws:
26+
if msg.type != aiohttp.WSMsgType.TEXT:
27+
continue
28+
29+
try:
30+
cookies: List[Dict[str, str]] = msg.json()
31+
except json.decoder.JSONDecodeError:
32+
continue
33+
34+
for c in cookies:
35+
session.cookie_jar.update_cookies(c)
36+
37+
return ws
38+
39+
app = web.Application()
40+
app.add_routes([web.get('/ws', websocket_handler)])
41+
42+
loop = asyncio.new_event_loop()
43+
asyncio.set_event_loop(loop)
44+
web.run_app(app, host='localhost', port=3001)
45+
46+
47+
def run_server(
48+
session: aiohttp.ClientSession,
49+
domain: str,
50+
cookies: List[str] = [],
51+
wait_for_cookies: bool = True,
52+
timeout: int = 30,
53+
):
54+
threading.Thread(
55+
target=_run_server,
56+
args=(session, cookies, domain,),
57+
daemon=True
58+
).start()
59+
60+
if wait_for_cookies:
61+
t = time.time()
62+
63+
while not len(session.cookie_jar):
64+
if (time.time() - t) > timeout:
65+
break
66+
67+
pass

extension/manifest.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "Cookie Sync",
3+
"description": "Synchronize cookies between chrome and Python",
4+
"version": "0.1",
5+
"manifest_version": 3,
6+
"host_permissions": ["*://*.google.com/*"],
7+
"permissions": ["cookies"],
8+
"background": {
9+
"service_worker": "build/sw.js"
10+
}
11+
}

extension/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "cookie_sync",
3+
"version": "0.1",
4+
"description": "Synchronize cookies between chrome and Python",
5+
"scripts": {
6+
"dev": "tsc -p . --watch",
7+
"build": "tsc -p ."
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"@types/chrome": "0.0.179"
14+
}
15+
}

extension/src/sw.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
interface CookieRequest {
2+
action: "get";
3+
cookies: string[];
4+
domain: string;
5+
}
6+
7+
const neededCookies: string[] = [];
8+
let ws: WebSocket;
9+
10+
const connect = () => {
11+
const _ws = new WebSocket("ws://localhost:3001/ws");
12+
ws = _ws;
13+
ws.onopen = () => {
14+
console.log("Connected! 👌");
15+
};
16+
17+
ws.onmessage = async (event): Promise<void> => {
18+
const cookieReq: CookieRequest = JSON.parse(event.data);
19+
20+
switch (cookieReq.action) {
21+
case "get":
22+
let retrievedCookies: { [key: string]: string }[] = [];
23+
24+
if (cookieReq.cookies.length) {
25+
const promises = cookieReq.cookies.map((c) =>
26+
chrome.cookies
27+
.get({ name: c, url: cookieReq.domain })
28+
.then((c) => c && retrievedCookies.push({ [c.name]: c.value }))
29+
);
30+
31+
await Promise.all(promises);
32+
} else {
33+
retrievedCookies = (await chrome.cookies.getAll({})).map((c) => ({
34+
[c.name]: c.value,
35+
}));
36+
}
37+
38+
ws.send(JSON.stringify(retrievedCookies));
39+
}
40+
};
41+
42+
ws.onclose = () => {
43+
console.log("Disconnected! 👎");
44+
setTimeout(connect, 10 * 1000);
45+
};
46+
47+
ws.onerror = () => {
48+
console.log("Error! 💩");
49+
ws.close();
50+
};
51+
};
52+
53+
connect();
54+
55+
chrome.cookies.onChanged.addListener((c) => {
56+
if (
57+
c.cause === "overwrite" &&
58+
(neededCookies.includes(c.cookie.name) || !neededCookies.length)
59+
) {
60+
ws.send(JSON.stringify({ [c.cookie.name]: c.cookie.value }));
61+
}
62+
});

extension/tsconfig.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": ["ES2016", "DOM"],
5+
"module": "commonjs",
6+
"outDir": "./build",
7+
"esModuleInterop": true,
8+
"forceConsistentCasingInFileNames": true,
9+
"strict": true,
10+
"skipLibCheck": true
11+
},
12+
"include": ["src/**/*.ts"],
13+
"exclude": ["node_modules"]
14+
}

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
aiohttp == 3.8.1

setup.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from setuptools import setup
2+
3+
4+
setup(
5+
name='cookie-sync',
6+
version='0.0.1',
7+
description='Sync cookies between Chrome',
8+
author='Sunney-X',
9+
author_email='sunneyxdev@gmail.com',
10+
url='https://github.com/Sunney-X/cookie-sync',
11+
packages=['cookie_sync'],
12+
long_description=open('README.md').read(),
13+
long_description_content_type="text/markdown",
14+
install_requires=[
15+
"aiohttp"
16+
],
17+
)

0 commit comments

Comments
 (0)