Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions manifest-chrome.partial.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"permissions": ["scripting", "webNavigation", "offscreen"],
"host_permissions": [
"http://localhost/*",
"https://cses.fi/*",
"https://codeforces.com/*",
"https://maang.in/*"
],
Expand Down
1 change: 1 addition & 0 deletions manifest-firefox.partial.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"permissions": [
"scripting",
"*://localhost/*",
"https://cses.fi/*",
"*://codeforces.com/*",
"*://maang.in/*",
"webNavigation"
Expand Down
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ![ICON](icon-48.png) cph-submit

Browser add on that enables Codeforces submit with
[Competitive Programming Helper](https://github.com/agrawal-d/cph)
Browser add-on that enables direct submission on Codeforces,CSES and Maang.in
with[Competitive Programming Helper](https://github.com/agrawal-d/cph).

Available for Firefox and Google Chrome.

Expand Down
102 changes: 102 additions & 0 deletions src/csesInjectedScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { ContentScriptData } from './types';
import log from './log';

declare const browser: any;
if (typeof browser !== 'undefined') {
self.chrome = browser;
}

log('cph-submit cses script injected');

const extMap: Record<string, string> = {
cpp: 'solution.cpp',
c: 'solution.c',
java: 'solution.java',
python: 'solution.py',
rust: 'solution.rs',
javascript: 'solution.js',
ruby: 'solution.rb',
haskell: 'solution.hs',
scala: 'solution.scala',
pascal: 'solution.pas',
};

const langMap: Record<string, string> = {
cpp: 'C++',
c: 'C',
java: 'Java',
python: 'Python3',
rust: 'Rust',
javascript: 'Node.js',
ruby: 'Ruby',
haskell: 'Haskell',
scala: 'Scala',
pascal: 'Pascal',
};
const idToKey: Record<number, string> = {
54: 'cpp',
50: 'cpp',
42: 'cpp',
61: 'cpp',
89: 'cpp',
91: 'cpp',
59: 'cpp',
2: 'cpp',
52: 'cpp',
43: 'c',
36: 'java',
60: 'java',
87: 'java',
7: 'python',
31: 'python',
40: 'python',
41: 'python',
70: 'python',
75: 'rust',
98: 'rust',
34: 'javascript',
55: 'javascript',
67: 'ruby',
12: 'haskell',
20: 'scala',
3: 'pascal',
4: 'pascal',
51: 'pascal',
};

chrome.runtime.onMessage.addListener((message) => {
if (message.type !== 'cph-submit-cses') return;

const { sourceCode, languageId } = message;

const form = document.querySelector('form');
if (!form) {
alert('CSES form not found');
log('CSES form not found');

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be an alert.

return;
}

const fileInput = form.querySelector(
'input[type="file"]',
) as HTMLInputElement;
if (fileInput) {
const langKey = idToKey[languageId] ?? 'cpp';

const fileName = extMap[langKey] ?? 'solution.cpp';
const file = new File([sourceCode], fileName, { type: 'text/plain' });
const dt = new DataTransfer();
dt.items.add(file);
fileInput.files = dt.files;
}

const langSelect = form.querySelector(
'select[name="lang"]',
) as HTMLSelectElement;
if (langSelect) {
const langKey = idToKey[languageId] ?? 'cpp';
langSelect.value = langMap[langKey] ?? 'C++';
}

form.submit();
log('CSES form submitted');
});
69 changes: 69 additions & 0 deletions src/handleSubmit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ export const isAlgoZenithProblem = (problemUrl: string) => {
return false;
}
};
export const isCSESProblem = (problemUrl: string) => {
console.log(`problemURL-CSES ${problemUrl} `);
try {
const url = new URL(problemUrl);
return url.hostname === 'cses.fi' || url.hostname.endsWith('.cses.fi');
} catch {
return false;
}
};

export const handleAlgoZenithSubmit = async (
problemName: string,
Expand Down Expand Up @@ -140,7 +149,59 @@ export const handleAlgoZenithSubmit = async (
},
);
};
export const handleCSESSubmit = async (
problemName: string,
languageId: number,
sourceCode: string,
problemUrl: string,
) => {
log('Handling CSES submit for', problemUrl);
log('isCSES check:', isCSESProblem(problemUrl));

const submitUrl = problemUrl
.replace('/task/', '/submit/')
.replace(/\/?$/, '/');

const tab = await chrome.tabs.create({
active: true,
url: submitUrl,
});

const tabId = tab.id as number;
chrome.windows.update(tab.windowId, { focused: true });

chrome.tabs.onUpdated.addListener(
function listener(updatedTabId, changeInfo) {
if (updatedTabId === tabId && changeInfo.status === 'complete') {
chrome.tabs.onUpdated.removeListener(listener);

setTimeout(async () => {
if (typeof chrome !== 'undefined' && chrome.scripting) {
await chrome.scripting.executeScript({
target: { tabId, allFrames: false },
files: ['/dist/csesInjectedScript.js'],
});
} else if (typeof browser !== 'undefined') {
await browser.tabs.executeScript(tabId, {
file: '/dist/csesInjectedScript.js',
});
}
setTimeout(() => {
chrome.tabs.sendMessage(tabId, {
type: 'cph-submit-cses',
problemName,
languageId,
sourceCode,
url: problemUrl,
});

log('Message sent to CSES tab');
}, 500);
}, 1000);
}
},
);
};
export const handleSubmit = async (
problemName: string,
languageId: number,
Expand All @@ -160,6 +221,14 @@ export const handleSubmit = async (
problemUrl,
);
}
if (isCSESProblem(problemUrl)) {
return handleCSESSubmit(
problemName,
languageId,
sourceCode,
problemUrl,
);
}

const submitTarget = resolveSubmitTarget(problemUrl);

Expand Down
1 change: 1 addition & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
backgroundScript: './src/backgroundScript.ts',
injectedScript: './src/injectedScript.ts',
algoZenithInjectedScript: './src/algoZenithInjectedScript.ts',
csesInjectedScript: './src/csesInjectedScript.ts',
offscreen: './src/offscreen.ts',
},
output: {
Expand Down
Loading