lotsa stuff

This commit is contained in:
iw0 2023-10-14 16:28:19 +02:00
parent 8d34088021
commit a519dbe814
11 changed files with 6094 additions and 67 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
web-ext-artifacts

1
README.md Normal file
View File

@ -0,0 +1 @@
# BC100 Autofill

View File

@ -1,55 +1,42 @@
"use strict";
// Put all the javascript code here, that you want to execute after page load. // Put all the javascript code here, that you want to execute after page load.
let bcFilled = false; let bcFilled = false;
let bdayFilled = false; let bdayFilled = false;
let clickThroughForms = false; let currentStage;
function clickContinue() { function executeStage(node) {
if (bdayFilled && bcFilled) { if (node.nodeType === 1) {
document.querySelector('.fahrgastrechte-bahn-card-auswahl button.fahrgastrechte-continue-button')?.dispatchEvent(new Event('click')); if (currentStage === undefined) {
currentStage = stages.shift();
}
if (currentStage.match(node)) {
console.log(currentStage.name, "matched");
console.log(currentStage.name, currentStage.execute(node) ? "executed" : "execution failed");
if (stages.length > 0) {
currentStage = stages.shift();
} else {
observer.disconnect();
}
} else {
console.log(currentStage.name, "did not match");
}
} }
} }
function processMutations(mutationList, observer) { function processMutations(mutationList, observer) {
for (const mutation of mutationList) { for (const mutation of mutationList) {
if (mutation.type === "childList") { if (mutation.type === "childList") {
mutation.addedNodes.forEach(node => { mutation.addedNodes.forEach(executeStage);
if (node.nodeType === 1) {
if (node.classList.contains("antrag-starten")) {
node.querySelector('button.test-antrag-starten-button').dispatchEvent(new Event('click', { bubbles: true }));
}
else if (node.classList.contains("fahrgastrechte-bahn-card-auswahl")) {
node.querySelectorAll('input').forEach(e => {
if (e.name === "fahrgastrechte-bahn-card-nummer") {
fillBcnum().then(clickContinue);
} else if (e.name === "fahrgastrechte-bahn-card-auswahl-geburts-datum") {
fillBday().then(clickContinue);
}
})
}
if (clickThroughForms) {
if (node.classList.contains("antrags-typ-auswahl")) {
node.querySelector('input#antragstyp-verspaetung').dispatchEvent(new Event('change'));
} else if (node.classList.contains("verspaetungs-auswahl")) {
node.querySelector('#verspaetungstyp-mehr-als-stunde').dispatchEvent(new Event('change'));
} else if (node.classList.contains("verspaetung-bestaetigung")) {
node.querySelector('button.fahrgastrechte-continue-button').dispatchEvent(new Event('click', { bubbles: true }));
}
} else {
console.log(node);
} }
} }
})
}
}
clickContinue();
} }
let clickThroughForms;
let observer = new MutationObserver(processMutations); let observer = new MutationObserver(processMutations);
const addObserver = () => { const addObserver = () => {
browser.storage.sync.get('autocontinue').then(v => { browser.storage.sync.get('autocontinue').then(v => {
clickThroughForms = !!v.autocontinue; clickThroughForms = !!v.autocontinue;
}).then(() => {
observer.observe(document.body, { observer.observe(document.body, {
childList: true, subtree: true childList: true, subtree: true
}) })
@ -58,25 +45,98 @@ const addObserver = () => {
addObserver(); addObserver();
async function fillBcnum() { const startClaim = {
let bcNumberInput = document.querySelector('input[name=fahrgastrechte-bahn-card-nummer]'); name: "startClaim",
match: node => node.classList.contains("antrag-starten"),
execute: node => {
const startenButton = node.querySelector('button.test-antrag-starten-button');
if (startenButton instanceof HTMLButtonElement) {
startenButton.dispatchEvent(new Event('click', { bubbles: true }));
return true;
}
return false;
}
}
function fillBcnum(bcNumberInput) {
browser.storage.sync.get('bcnum').then(v => { browser.storage.sync.get('bcnum').then(v => {
let bcNum = v.bcnum || null; let bcNum = v.bcnum || null;
if (bcNum !== null && bcNum !== "") { if (bcNum !== null && bcNum !== "") {
bcNumberInput.value = bcNum; bcNumberInput.value = bcNum;
bcNumberInput.dispatchEvent(new Event('input', { bubbles: true })); bcNumberInput.dispatchEvent(new Event('input', { bubbles: true }));
bcFilled = true; return true;
} }
}) })
return false;
} }
async function fillBday() { function fillBday(birthdayInput) {
let birthdayInput = document.querySelector('input[name=fahrgastrechte-bahn-card-auswahl-geburts-datum');
browser.storage.sync.get('bday').then(v => { browser.storage.sync.get('bday').then(v => {
bDay = v.bday || null; const bDay = v.bday || null;
if (bDay !== null && bDay !== "") { if (bDay !== null && bDay !== "") {
birthdayInput.value = bDay; birthdayInput.value = bDay;
birthdayInput.dispatchEvent(new Event('input', { bubbles: true })); birthdayInput.dispatchEvent(new Event('input', { bubbles: true }));
bdayFilled = true; bdayFilled = true;
return true;
} }
}) })
return false;
} }
const fillData = {
name: "fillData",
match: node => node.classList.contains("fahrgastrechte-bahn-card-auswahl"),
execute: node => {
let bcNumField, bdayField;
node.querySelectorAll('input').forEach(e => {
if (e.name === "fahrgastrechte-bahn-card-nummer") {
bcNumField = e;
} else if (e.name === "fahrgastrechte-bahn-card-auswahl-geburts-datum") {
bdayField = e;
}
})
fillBcnum(bcNumField);
fillBday(bdayField);
return true;
}
}
const clickContinue = {
name: "clickContinue",
match: () => true,
execute: e => {
const continueButton = document.querySelector('.fahrgastrechte-bahn-card-auswahl button.fahrgastrechte-continue-button');
if (continueButton instanceof Element) {
continueButton.dispatchEvent(new Event('click'));
return true;
}
return false;
}
}
const iWasDelayed = {
name: "iWasDelayed",
match: node => node.classList.contains("antrags-typ-auswahl") && clickThroughForms,
execute: node => {
const delay = node.querySelector('input#antragstyp-verspaetung');
if (delay instanceof HTMLInputElement) {
delay.dispatchEvent(new Event('change'));
return true;
}
return false;
}
}
const moreThan60Minutes = {
name: "moreThan60Minutes",
match: node => node.classList.contains("verspaetungs-auswahl") && clickThroughForms,
execute: node => node.querySelector('#verspaetungstyp-mehr-als-stunde').dispatchEvent(new Event('change'))
}
const continueToForm = {
name: "continueToForm",
match: node => node.classList.contains("verspaetung-bestaetigung") && clickThroughForms,
execute: node => node.querySelector('button.fahrgastrechte-continue-button').dispatchEvent(new Event('click', { bubbles: true }))
}
let stages = [
startClaim, fillData, clickContinue, iWasDelayed, moreThan60Minutes, continueToForm
];

View File

@ -2,14 +2,16 @@
"manifest_version": 2, "manifest_version": 2,
"name": "BC100 Autofill", "name": "BC100 Autofill",
"description": "Autofills your BahnCard 100 number and birthday into the passenger rights claim form.", "description": "Autofills your BahnCard 100 number and birthday into the passenger rights claim form.",
"version": "0.0.1", "version": "0.0.2",
"icons": { "icons": {
"64": "icons/icon.png" "64": "icons/icon.png"
}, },
"content_scripts": [ "content_scripts": [
{ {
"matches": [ "matches": [
"https://www.bahn.de/buchung/fahrgastrechte?einstiegtyp=BC100" "https://www.bahn.de/buchung/fahrgastrechte?einstiegtyp=BC100",
"https://www.bahn.de/buchung/fahrgastrechte?lang=*&einstiegtyp=BC100",
"https://int.bahn.de/*/buchung/fahrgastrechte?einstiegtyp=BC100"
], ],
"js": [ "js": [
"content_script.js" "content_script.js"

View File

@ -8,21 +8,25 @@
<body> <body>
<form> <form>
<fieldset> <label class="item">
<label for="bcnum">The number of your BahnCard 100</label> <span class="fh">Your BahnCard 100 number</span><br />
<input name="bcnum" id="bcnum" type="text" pattern="70814[0-9]{11}" /> <span class="sh">The number on the front of your card starting with <span class="ms">7081</span>.</span><br>
</fieldset> <input name="bcnum" id="bcnum" type="text" pattern="70814[0-9]{11}" required
<fieldset> title="Your BahnCard number is 16 digits long and starts with 7081." />
<label for="birthday">Your date of birth</label> </label>
<input name="birthday" id="birthday" type="date" /> <label class="item">
</fieldset> <span class="fh">Your date of birth</span><br>
<fieldset> <input name="birthday" id="birthday" type="date" required />
<label for="autocontinue">Automatically continue to the "I was delayed by more than 60 minutes" screen</label> </label>
<input name="autocontinue" id="autocontinue" type="checkbox" /> <label>
</fieldset> <span class="fh">Skip questionnaire</span><br>
<button type="submit">Save</button> <span class="sh">Automatically answer "I was delayed, by more than 60 minutes."</span><br>
<input name="autocontinue" id="autocontinue" type="checkbox" required />
</label>
<div class="item"><button type="submit">Save</button><span id="success"></span></div>
<p id="errors"></p>
</form> </form>
<script src="script.js"></script> <script type="module" src="script.js"></script>
</body> </body>
</html> </html>

View File

@ -1,16 +1,63 @@
async function saveOptions(e) { function luhnValidate(cardNumber) {
console.log("Saving options."); cardNumber = String(cardNumber).replace(/[\s]/g, "");
e.preventDefault(); let odd = false;
let bcNum = document.querySelector("#bcnum").value; let total = 0;
let bDay = document.querySelector('#birthday').value; let position, doubledPos;
let autoContinue = document.querySelector('#autocontinue').checked;
if (!/^[0-9]+$/.test(cardNumber)) {
return false;
}
for (var i = cardNumber.length; i > 0; i--) {
position = parseInt(cardNumber[i - 1]);
if (!odd) {
total += position;
} else {
doubledPos = position * 2;
if (doubledPos > 9) {
doubledPos -= 9;
}
total += doubledPos;
}
odd = !odd;
}
return (total !== 0 && (total % 10) === 0);
}
function putError(s){
document.querySelector('#errors').textContent += s;
}
function clearError(){
document.querySelector('#errors').textContent = "";
}
async function saveOptions(ev) {
clearError();
ev.preventDefault();
let bcNum = this.querySelector("#bcnum").value;
if (!bcNum.length == 16 || !bcNum.startsWith("70814") || !luhnValidate(bcNum)) {
putError("Your BahnCard number is not valid. Please make sure you haven't made any typos.");
return;
}
let bDay = this.querySelector('#birthday').value;
if (bDay == ""){
putError("Please enter a birthday.");
return;
}
let autoContinue = this.querySelector('#autocontinue').checked;
let options = { let options = {
bcnum: bcNum, bcnum: bcNum,
bday: bDay, bday: bDay,
autocontinue: autoContinue autocontinue: autoContinue
}; };
await browser.storage.sync.set(options); browser.storage.sync.set(options).then(() => {
console.log("Saved!"); document.querySelector('#success').textContent = "✔";
setTimeout(() => {
document.querySelector('#success').textContent = '';
}, 5000);
})
} }
async function restoreOptions() { async function restoreOptions() {
@ -18,7 +65,7 @@ async function restoreOptions() {
// let res = await browser.storage.managed.get('colour'); // let res = await browser.storage.managed.get('colour');
// document.querySelector("#managed-colour").innerText = res.colour; // document.querySelector("#managed-colour").innerText = res.colour;
settings = await browser.storage.sync.get(); let settings = await browser.storage.sync.get();
console.log(settings); console.log(settings);
document.querySelector("#bcnum").value = settings.bcnum || ""; document.querySelector("#bcnum").value = settings.bcnum || "";
document.querySelector("#birthday").value = settings.bday || ""; document.querySelector("#birthday").value = settings.bday || "";

View File

@ -1,3 +1,52 @@
h1 { .fh {
font-size: 1.5em;
font-weight: bold;
}
.sh {
font-size: 1em;
font-style: italic; font-style: italic;
} }
label.item {
display: block;
}
input {
margin: 8px 0px;
}
.ms {
font-family: monospace;
}
#errors {
color: red;
font-style: italic;
font-weight: bold;
}
input:invalid {
background-color: rgba(212, 59, 59, 70%);
}
input:focus {
background-color: revert;
}
#success {
color: lightgreen;
font-size: 1.5em;
margin-left: 8px;
}
:root {
color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
}
}

5839
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"devDependencies": {
"@types/firefox-webext-browser": "^111.0.2",
"web-ext": "^7.8.0",
"webextension-polyfill": "github:mozilla/webextension-polyfill"
},
"scripts": {
"start:firefox": "web-ext run",
"debug": "web-ext run --devtools --firefox deved --url https://www.bahn.de/buchung/fahrgastrechte?einstiegtyp=BC100"
},
"dependencies": {
"luhn": "^2.4.1",
"typescript": "^5.2.2"
}
}

0
stages.js Normal file
View File

8
web-ext-config.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
ignoreFiles: [
"README.md",
"package.json",
"web-ext-config.js",
".DS_Store"
]
}