大学のライブキャンパス、全自動でログインできるようにした

## 大学のライブキャンパス

ほとんどの大学はライブキャンパスがあると思いますが、私の大学ではログイン情報を入力した後ログインボタンを押下しないと自分のページに遷移できません...。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985494/14b313a4-7c2c-609a-00a8-d767f77f9f8e.png)

こういうやつですね。。。。

Google Chromeを使っていれば、ログイン情報を記憶しておいて自動で入力してくれる機能があるため、実質ユーザーがしなければならない操作はログインボタンの押下だけなのですが....


**これすらもめんどくさく感じるようになってしまいました。**

## 拡張機能を自作

Chrome拡張機能を自作し、ログイン情報の自動入力からログインボタンの押下までを完全に自動でやってくれる拡張機能を自作することにしました。

そこで、以下のような実装で拡張機能を作っていきます。

1. 拡張機能をインストールしたユーザーは、まず自分のユーザー名・パスワードを`chrome.storage`APIに保存。
1. 拡張機能はバックグラウンドでユーザーのアクセスするurlを監視し、ライブキャンパスにアクセスしたらこれを検知。
1. ライブキャンパスへのアクセスを検知したら、保存していたログイン情報をエントリーに入力し、ログインボタンを自動で押下。 

これらの機能はそれぞれ`options.html`と`options.js`、`background.js`、`content.js`が担当します。これらとは別に、拡張機能には設定のため`manifest.json`というファイルが必要になるため、これも作ります。

ファイルのディレクトリ構造は以下のようになります。

```
.
│  manifest.json
│
├─background
│      background.js
│
├─content
│      content.js
│
└─options
        options.html
        options.js
```

## 問題のライブキャンパスを調査

伝家の宝刀デベロッパーツールを使ってhtmlを調べていきます。

**`ctl` + `shift` + `I` !!!!**

アカウント名のエントリーをコードしている個所を特定しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985494/7a73b704-5bbf-ee17-991b-686dc623263b.png)


`id="userID"` の input 要素を取得すれば良さそうですね。。。。

パスワードは、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985494/5b5a24cf-2c7b-fc5e-3982-d1c9c1ce91f8.png)


パスワードはIDが設定されていなかったので`querySelector`でinput 要素を取得することにしました。

## 設定ファイル

``` manifest.json
{
  "manifest_version": 3,
  "name": "Live Campus Auto Login Extension",
  "version": "1.0",
  "permissions": [
    "scripting",
    "tabs", 
    "storage"
  ],
  "host_permissions": [
    "私の大学のURL"
  ],
  "background": {
    "service_worker": "background/background.js"
  },
  "options_ui": {
    "page": "options/options.html",
    "open_in_tab": true
  }, 
  "action": {}
}
```
（ライブキャンパスのURLだけ書き換えています）

拡張機能のボタンをクリックしたとき、設定画面を新しいタブで開きたかったので、`open_in_tab`を`True`にしています。

また、ログイン情報を保存するために`storage`を許可しています。

## ログイン情報入力＆保存

``` options.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ログイン情報設定</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
    }
    label {
      display: block;
      margin: 10px 0 5px;
    }
    input[type="text"],
    input[type="password"] {
      width: 100%;
      padding: 8px;
      margin-bottom: 15px;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
    button {
      background-color: #4CAF50;
      color: white;
      border: none;
      padding: 10px 20px;
      cursor: pointer;
      border-radius: 4px;
    }
    button:hover {
      background-color: #45a049;
    }
    .status {
      margin-top: 10px;
      color: green;
    }
  </style>
</head>
<body>
  <h1>ログイン情報の設定</h1>
  <form id="settings-form">
    <label for="username">ユーザー名:</label>
    <input type="text" id="username" name="username" placeholder="ユーザー名を入力してください">

    <label for="password">パスワード:</label>
    <input type="password" id="password" name="password" placeholder="パスワードを入力してください">

    <button type="button" id="save-button">保存</button>
  </form>

  <div class="status" id="status"></div>

  <script src="options.js"></script>
</body>
</html>

```

``` options.js
// 保存ボタンのクリックイベント
document.getElementById("save-button").addEventListener("click", function () {
    const username = document.getElementById("username").value;
    const password = document.getElementById("password").value;

    // 入力値をchrome.storageに保存
    chrome.storage.sync.set({ username, password }, function () {
        const status = document.getElementById("status");
        status.textContent = "ログイン情報を保存しました！";
        setTimeout(() => (status.textContent = ""), 3000);

        // アラートを表示
        alert("ログイン情報が保存されました。\nユーザー名: " + username + "\nパスワード: " + password);
    });
});

// 保存された値をフォームに表示
document.addEventListener("DOMContentLoaded", function () {
    chrome.storage.sync.get(["username", "password"], function (data) {
        if (data.username) {
            document.getElementById("username").value = data.username;
        }
        if (data.password) {
            document.getElementById("password").value = data.password;
        }
    });
});
```

スクリプトをhtmlファイルに直で書くとセキュリティー上問題があるとのことでエラーになってしまったので、そこだけ注意ですね。他は特に気にすることはないです（）。

## バックグラウンド

``` background.js
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === "complete" && tab.url && tab.url.includes("大学のライブキャンパスのURL")) {
    chrome.scripting.executeScript({
      target: { tabId: tabId },
      files: ["content/content.js"]
    }, () => {
      if (chrome.runtime.lastError) {
        console.error("Script injection failed:", chrome.runtime.lastError.message);
      }
    });
  }
});

chrome.action.onClicked.addListener(() => {
  chrome.runtime.openOptionsPage();
});

```

（ライブキャンパスのURLだけ書き換えています）

## 自動入力、ログインボタン押下

``` content.js
// 保存されたユーザー名とパスワードを取得
chrome.storage.sync.get(["username", "password"], function(data) {
    const studentID = data.username || ""; // 保存されたユーザー名
    const studentPass = data.password || ""; // 保存されたパスワード
  
    // `id="userID"` の input 要素を取得
    const inputElementUserID = document.getElementById("userID");
    const inputElementPass = document.querySelector('input[name="password"]');
  
    if (inputElementUserID) {
      inputElementUserID.value = studentID;
    }
  
    if (inputElementPass) {
      inputElementPass.value = studentPass;
    }
  
    // ログインボタンを押下
    const loginButton = document.querySelector('a.btn_login');
    if (loginButton) {
      loginButton.click();
    }
  });
```

## 実際に動かしてみた!!!

Chromeの拡張機能をONにして、自分のPCのストレージから読み込み。。。。

**動いたああああ！！！**

拡張機能を作るのは初めてだったので、5時間くらいかかりましたが嬉しいです。

## 他大学の学生の需要

今ツイッターで在校生に需要をアンケート採っているのですが、他大学のライブキャンパス使っている人で使いたいって人いるんでしょうか。。。

今は私の大学のポータルサイトでしか動かないのですが、ほかの大学のポータルサイトでも動くように改造するのは技術的に可能です。

もし需要があれば、ほかのポータルサイトでも動くようにしたうえで、GoogleのChrome Web Storeにデプロイするのもありかなと考えています。

ぜひパブリックにアップロードしてほしい、などの要望がありましたら、ご連絡いただければと思います。

（とはいえ、ボタン一つ押す手間を省くために拡張機能を作るのもどうかと思いますが。。。。）

記事の内容は以上です。ご覧いただきありがとうございました。