Skip to content
This repository has been archived by the owner on Oct 4, 2021. It is now read-only.

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
k1832 committed Aug 29, 2021
0 parents commit 5b3ff38
Show file tree
Hide file tree
Showing 7 changed files with 3,338 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ATCODER_ID=YourIDHere
ATCODER_PASSWORD=YourPasswordHere
60 changes: 60 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module.exports = {
'env': {
'browser': true,
'commonjs': true,
'es2021': true,
},
'parserOptions': {
'ecmaVersion': 12,
},
"rules": {
"indent": [
"error",
4
],
"function-call-argument-newline": [
"error",
"consistent"
],
"quotes": [
"error",
"double",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"semi": [
"error"
],
"no-var": "error",
"prefer-const": "error",
"prefer-spread": "error",
"prefer-template": "error",
"no-console": "off",
"no-undefined": "error",
"dot-notation": "error",
"no-extra-bind": "error",
"no-multi-spaces": "error",
"no-useless-return": "error",
"no-irregular-whitespace": [
"error",
{
"skipRegExps": true
}
],
"wrap-iife": "error",
"no-lonely-if": "error",
"space-in-parens": [
"error",
"never"
],
"space-infix-ops": "error"
}
};
125 changes: 125 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@

# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test
.env.production

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# End of https://www.toptal.com/developers/gitignore/api/node
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# what is this?
AtCoder Beginner Contestトーナメント(以下ABCT)の登録をやってくれます。

**※使用は自己責任でお願いします。**
**※開発時、私がABCTに登録済みだったため最後までテストできていません。**

ABCT登録時の以下のようなフローを自動でやってくれます。
1. ABCTにAtCoderのIDを入力して登録ボタンを押下
2. AtCoderでの所属を表示された文字列に変更
3. ABCTに戻って確認ボタンを押下
4. (AtCoderの所属を元に戻す)

# versions
- node: 15.6.0
- npm: 7.4.0

# how to run
1. ソースコードをクローン
`git clone https://github.com/k1832/abc-tournament-registration.git`
2. フォルダを移動
`cd abc-tournament-registration`
3. 依存関係をインストール
`npm i`
4. ユーザ情報を準備(コマンド入力後に、 **.env** の内容を自分のIDとパスワードに変更してください。)
`mv .env.sample .env`
5. 実行
`node index.js`
141 changes: 141 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
const fs = require("fs");
const {exit} = require("process");
require("dotenv").config();
const puppeteer = require("puppeteer");

if (!fs.existsSync(".env")) {
console.log(".env ファイルを用意してください。");
exit();
}

// get user info from env file
const ATCODER_ID = process.env.ATCODER_ID;
const ATCODER_PASSWORD = process.env.ATCODER_PASSWORD;

if (ATCODER_ID == "YourIDHere" || ATCODER_PASSWORD == "YourPasswordHere") {
console.log(".env の内容を自分のIDとパスワードに変更してください。");
exit();
}

let BROWSER;

const VIEWPORT = {
width: 1280,
height: 1024,
};

const selector = {
atcoder_tournament: {
idInputForm: ".MuiInputBase-input.MuiOutlinedInput-input",
registerButton: "button.MuiButtonBase-root",
temporaryAffiliationCode: "code"
},
atcoder: {
affiliationForm: "input#ui\\.Affiliation",
usernameForm: "input#username",
passwordForm: "input#password",
loginButton: "button#submit",
updateButton: "button#submit"
},
};

(async () => {
// open registration form
const options = {
headless: false,
slowMo: 20,
};
BROWSER = await puppeteer.launch(options);
const abcTournamentPage = await BROWSER.newPage();
await abcTournamentPage.setViewport({
width: VIEWPORT.width,
height: VIEWPORT.height,
});
const abcTournamentUrl = "https://abc.kenkoooo.com/#/register";
await abcTournamentPage.goto(abcTournamentUrl, {waitUntil: "domcontentloaded"});

// registration
await typeTextWithSelector(abcTournamentPage, selector.atcoder_tournament.idInputForm, ATCODER_ID);
await clickWithSelector(abcTournamentPage, selector.atcoder_tournament.registerButton, false);

// get temporary affiliation
const tempAffiliation = await getTextWithSelector(abcTournamentPage, selector.atcoder_tournament.temporaryAffiliationCode);

// open atcoder setting page
const atcoderPage = await BROWSER.newPage();
await atcoderPage.setViewport({
width: VIEWPORT.width,
height: VIEWPORT.height,
});
const atcoderUrl = "https://atcoder.jp/settings";
await atcoderPage.goto(atcoderUrl, {waitUntil: "domcontentloaded"});

// login to atcoder
await typeTextWithSelector(atcoderPage, selector.atcoder.usernameForm, ATCODER_ID);
await typeTextWithSelector(atcoderPage, selector.atcoder.passwordForm, ATCODER_PASSWORD);
await clickWithSelector(atcoderPage, selector.atcoder.loginButton);

// get current (original) affiliation
const originalAffiliation = await getInputFormTextWithSelector(atcoderPage, selector.atcoder.affiliationForm);

// change affiliation to temporary one
await clearInputFormTextWithSelector(atcoderPage, selector.atcoder.affiliationForm);
await typeTextWithSelector(atcoderPage, selector.atcoder.affiliationForm, tempAffiliation);
await clickWithSelector(atcoderPage, selector.atcoder.updateButton);

// confirm temporary affiliation in abc tournament
await abcTournamentPage.bringToFront();
await clickWithSelector(abcTournamentPage, selector.atcoder_tournament.registerButton, false);
// TODO: add wait for confirmation

await abcTournamentPage.waitFor(3000);

// restore original affiliation
await atcoderPage.bringToFront();
await clearInputFormTextWithSelector(atcoderPage, selector.atcoder.affiliationForm);
await typeTextWithSelector(atcoderPage, selector.atcoder.affiliationForm, originalAffiliation);
await clickWithSelector(atcoderPage, selector.atcoder.updateButton);

// wait for 3 sec for no reason
await atcoderPage.waitFor(3000);


BROWSER.close();
})();

async function getTextWithSelector(page, selector) {
const element = await page.waitForSelector(selector, {visible: true});
let text = "";
if (element) {
text = await (await element.getProperty("textContent")).jsonValue();
text = text.replace(/[\s ]/g, "");
}
return text;
}

async function getInputFormTextWithSelector(page, selector) {
const elm = await page.waitForSelector(selector);
return await elm.evaluate((input) => input.value);
}

async function clearInputFormTextWithSelector(page, selector) {
const elm = await page.waitForSelector(selector);
await elm.evaluate((input) => input.value = "");
}

async function typeTextWithSelector(page, selector, text) {
const elm = await page.waitForSelector(selector);
await elm.type(text);
}

async function clickWithSelector(page, selector, withWait = true) {
const elm = await page.waitForSelector(selector);
if (withWait) {
await Promise.all([
page.waitForNavigation(),
elm.click(),
]);
} else {
await elm.click();
}
}
Loading

0 comments on commit 5b3ff38

Please sign in to comment.