feat: replace browser login with imported Reddit sessions
This commit is contained in:
1 parent
2b0545afcf
commit
c483c7b8be
76 files changed
+2048
-2828
No files matched your search
@@ -0,0 +1,15 @@
|
||||
# RedditBot Session Exporter
|
||||
|
||||
This local Chrome extension exports `reddit.com` cookies and the current Reddit Chat Matrix credential into one JSON file for the self-hosted dashboard's **Import Reddit session** control. The file is equivalent to a login credential: do not share it, commit it, or send it to anyone.
|
||||
|
||||
## Install and use
|
||||
|
||||
1. In Chrome, open `chrome://extensions`, enable **Developer mode**, then choose **Load unpacked** and select this folder.
|
||||
If it was already loaded, click its **Reload** button after updating these files so Chrome starts the new service worker.
|
||||
2. Sign in to Reddit in the same Chrome profile.
|
||||
3. Open [Reddit Chat](https://www.reddit.com/chat/) and wait for it to finish loading. The extension badge changes to `OK` when it captures the Matrix credential.
|
||||
4. Click the **RedditBot Session Exporter** toolbar icon and save `redditbot-session.json`.
|
||||
5. In RedditBot Dashboard > Settings > Access, choose **Import Reddit session** and select the exported file.
|
||||
6. Delete the downloaded JSON file once the dashboard confirms that the session is authenticated.
|
||||
|
||||
The extension observes only requests to Reddit's Matrix homeservers, verifies the captured token with Matrix `whoami`, reads Reddit cookies, and saves the combined export locally. It does not send the dump to any server; you upload it directly to your RedditBot dashboard.
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "RedditBot Session Exporter",
|
||||
"version": "1.0.0",
|
||||
"description": "Exports Reddit cookies and Reddit Chat credentials for a self-hosted RedditBot dashboard.",
|
||||
"permissions": ["cookies", "downloads", "storage", "webRequest"],
|
||||
"host_permissions": [
|
||||
"https://reddit.com/*",
|
||||
"https://*.reddit.com/*",
|
||||
"https://matrix.redditspace.com/*",
|
||||
"https://matrix.reddit.com/*"
|
||||
],
|
||||
"action": { "default_title": "Export RedditBot session" },
|
||||
"background": { "service_worker": "service-worker.js", "type": "module" }
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { createSessionExportDownload } from './session-export.js';
|
||||
|
||||
const MATRIX_URLS = [
|
||||
'https://matrix.redditspace.com/*',
|
||||
'https://matrix.reddit.com/*',
|
||||
];
|
||||
|
||||
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||
async (details) => {
|
||||
const authorization = details.requestHeaders?.find(
|
||||
(header) => header.name.toLowerCase() === 'authorization',
|
||||
)?.value;
|
||||
if (!authorization?.toLowerCase().startsWith('bearer ')) return;
|
||||
|
||||
const url = new URL(details.url);
|
||||
await chrome.storage.local.set({
|
||||
matrixCapture: {
|
||||
homeserver: `${url.protocol}//${url.host}`,
|
||||
accessToken: authorization.slice('bearer '.length).trim(),
|
||||
capturedAt: Date.now(),
|
||||
},
|
||||
});
|
||||
await chrome.action.setBadgeText({ text: 'OK' });
|
||||
await chrome.action.setBadgeBackgroundColor({ color: '#198754' });
|
||||
},
|
||||
{ urls: MATRIX_URLS },
|
||||
['requestHeaders', 'extraHeaders'],
|
||||
);
|
||||
|
||||
async function showError(message) {
|
||||
await chrome.action.setBadgeText({ text: 'ERR' });
|
||||
await chrome.action.setBadgeBackgroundColor({ color: '#dc3545' });
|
||||
await chrome.action.setTitle({ title: `RedditBot export not ready: ${message}` });
|
||||
}
|
||||
|
||||
chrome.action.onClicked.addListener(async () => {
|
||||
try {
|
||||
const cookies = await chrome.cookies.getAll({ domain: 'reddit.com' });
|
||||
const { matrixCapture } = await chrome.storage.local.get('matrixCapture');
|
||||
if (!matrixCapture) {
|
||||
throw new Error('Open Reddit Chat, wait for it to load, then click the extension again.');
|
||||
}
|
||||
|
||||
const whoami = await fetch(`${matrixCapture.homeserver}/_matrix/client/v3/account/whoami`, {
|
||||
headers: { Authorization: `Bearer ${matrixCapture.accessToken}` },
|
||||
});
|
||||
if (!whoami.ok) throw new Error('The captured Reddit Chat token has expired. Reload Reddit Chat and try again.');
|
||||
const identity = await whoami.json();
|
||||
if (!identity?.user_id) throw new Error('Reddit Chat did not return an account identity.');
|
||||
|
||||
await chrome.downloads.download({
|
||||
...createSessionExportDownload(cookies, { ...matrixCapture, userId: identity.user_id }),
|
||||
saveAs: true,
|
||||
});
|
||||
} catch (error) {
|
||||
await showError(error instanceof Error ? error.message : 'Could not create the session dump.');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
const REDDITBOT_SESSION_FILENAME = 'redditbot-session.json';
|
||||
|
||||
function isRedditDomain(domain) {
|
||||
const normalised = domain.toLowerCase().replace(/^\./, '');
|
||||
return normalised === 'reddit.com' || normalised.endsWith('.reddit.com');
|
||||
}
|
||||
|
||||
function toExportCookie(cookie) {
|
||||
return {
|
||||
name: cookie.name,
|
||||
value: cookie.value,
|
||||
domain: cookie.domain,
|
||||
path: cookie.path,
|
||||
expires: cookie.expirationDate,
|
||||
httpOnly: cookie.httpOnly,
|
||||
secure: cookie.secure,
|
||||
sameSite: cookie.sameSite,
|
||||
};
|
||||
}
|
||||
|
||||
export function createSessionExportDownload(cookies, matrix) {
|
||||
if (!matrix?.homeserver || !matrix?.accessToken || !matrix?.userId) {
|
||||
throw new Error('Open Reddit Chat before exporting so Matrix credentials can be captured.');
|
||||
}
|
||||
const exportFile = {
|
||||
cookies: cookies.filter((cookie) => isRedditDomain(cookie.domain)).map(toExportCookie),
|
||||
matrix,
|
||||
};
|
||||
|
||||
return {
|
||||
filename: REDDITBOT_SESSION_FILENAME,
|
||||
url: `data:application/json;charset=utf-8,${encodeURIComponent(JSON.stringify(exportFile))}`,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user