Qiita予約投稿機能を作ってみた

↓バージョンアップデートしました。

https://qiita.com/iketo/items/9a9227aa9499c35a70e4

エンジニアとして働いていると度々お世話になるQiita記事。その有難さには頭が上がりませんが、私のエンジニア歴も1年に達しQiitaの投稿を少しずつ始めた中で一つだけ不満がありました。
**Qiitaにはなぜ予約投稿機能がないのだろうか？**
...ということで、JavaScriptの練習をかねてQiitaの予約投稿機能をChrome拡張で作ってみました。

## 使い方

https://chromewebstore.google.com/detail/qiita%E4%BA%88%E7%B4%84%E6%8A%95%E7%A8%BF/fdlailgogjlojolaiehgpkhbcikjgdeh?hl=ja&utm_source=ext_sidebar

1.上記リンクより拡張機能を追加してください。
2.拡張機能を有効にすると、Qiita記事編集ページに予約投稿ボタンが現れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519892/4c48bc78-a002-0450-2034-8613e2a926f0.png)
3.投稿したい時刻をHHmm形式で入力します(投稿予約時刻はコンソールに表示されます)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519892/ac5b826a-3b07-895b-75bd-b052b8a28122.png)
4.予約した時刻になると記事が投稿されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519892/7403c61f-c85d-2a85-4d8f-d4a25bd91dff.png)

## 中身

・画面のロード後に、予約投稿ボタンを作成する函数を呼び出します。
``` JavaScript
var containerDiv, setting, posting, button, hour, minute;

window.onload = function() {
    createButton();
};
```
・画面の要素を取得して、公開設定ボタンの横に予約投稿ボタンを作成します。

:::
containerDiv : 「下書き保存」「公開設定」を覆うdiv
setting : 「公開設定」ボタン
posting : 「記事を投稿する」ボタン
:::

``` JavaScript
function createButton() {
    containerDiv = document.querySelector('style-ucg0tn');
    setting = document.getElementsByClassName('style-1fu20hp')[0];
    posting = document.getElementsByClassName('style-mtcazu')[0];
    button = document.createElement('button');
    button.textContent = '予約投稿';
    button.id = 'autoPost'; 

    // ボタンをDOMに追加
    if(containerDiv) {
        containerDiv.appendChild(button);  
    }
    
    // ボタンがクリックされたときの処理
    button.addEventListener('click', function() {
        autoPost();
    });
}

```
・予約投稿ボタンがクリックされたときの処理を記述します。予約時刻を設定する函数を呼び出し、指定時間後に投稿の動作を行う函数を呼び出します。
``` JavaScript
function autoPost() {
    if(setting && posting) {
        // スケジュールを設定
        getPostingSchedule();
        if(hour && minute) {
            // 予約時刻になったら投稿
            waitUntilHour(hour, minute, function() {
                setting.click();
                posting.click();
                window.alert("記事を投稿しました！");
            });
        }
    }
}

```
・指定時間後に処理を行う函数です。予約時刻と現在の時刻の時間差を取得し、Timeoutで指定時間後に呼び出し元で記述された処理を行います。
``` JavaScript
function waitUntilHour(hour, minute, callback) {
    console.log(`投稿予定時間は${hour}:${minute}です。`);
    var now = new Date();
    var targetTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hour, minute, 0, 0);
    var timeDiff = targetTime.getTime() - now.getTime();

    // すでに今日の指定時間を過ぎていた場合
    if (timeDiff <= 0) {
        // 明日の同じ時間に設定
        targetTime.setDate(targetTime.getDate() + 1);
        timeDiff = targetTime.getTime() - now.getTime();
    }

    setTimeout(callback, timeDiff);
}

```
・投稿予約時刻を設定する函数です。ポップアップで入力を受け付けた後、バリデーションチェックを行い予約時刻を設定します。
``` JavaScript
function getPostingSchedule() {
    var input = prompt("投稿予定時間をHHmm形式で入力してください。例: 0830");

    // 入力値のチェック
    if (!input || input === "") {
        alert("時間を入力してください。");
        return;
    } else if (input.length !== 4 || !/^\d+$/.test(input)) {
        alert("正しい形式で時間を入力してください。");
        return;
    }
    
    // 入力をHHとMMに分割
    var hourInput = parseInt(input.substring(0, 2), 10);
    var minuteInput = parseInt(input.substring(2, 4), 10);

    // 時間範囲のチェック
    if (hourInput < 0 || hourInput > 23 || minuteInput < 0 || minuteInput > 59) {
        alert("正しい形式で時間を入力してください。");
        return;
    } else if (hourInput < 6 || hourInput > 22) {
        alert("深夜・早朝の投稿は控えましょう。");
        return;
    } else {
        hour = hourInput;
        minute = minuteInput;
    }
}

```

## おわりに
個人の用途としてはギリギリ使える範囲ですが、現状ではブラウザを閉じると動作しない等まだまだ改善の余地が多くあります。軽微なバグ修正などはこの記事の更新、それなりの改修をした場合は新たに記事を投稿して、継続的にアップデートしていきたいです。
指摘・要望も大歓迎です！

--今後の予定
・時刻をサーバーから取得したい
・投稿をクリックでなくAPIで行えないか
・バックグラウンド実行？