😽

○ 職 DRAFT の指名状況が気になって仕事にならないのでデスクトップ通知アプリを作る

2022/11/18に公開

導入

こんにちは。タイトルにもありますが絶賛転職活動中の者です。

自分の興味のある会社を順次受けて行っていますが、行きたい会社だけだと数的に不安 && 良い出会いがあるかもしれないので並行して転職サービスも利用しています。

その内の一つに転職 DRAFTというサービスがあります。
https://job-draft.jp/

サービス自体の説明はググるとたくさん出てくるので簡単に説明すると、レジュメを載せておけば企業からスカウトがくるサービスです。

問題

来る11月16日にスカウト期間が始まったのですが、この期間中はマイページの「あなたを気にしている企業」
が随時更新されていきます。

「(企業名)があなたを検討中に入れました!」「(企業名)があなたのZennを見ました!」
等の情報がリアルタイムで更新されおり、初参加ということもありこれはおもろいなぁ
と思ったのもつかの間。

ついふとした瞬間にマイページを見に行っちゃって、作業に集中できない。

気になっちゃうんです....こういうの。

作業に集中できないのも困るため、マイページが更新されたらデスクトップに通知する仕組みを作ってわざわざ毎回見にいかなくても状況が知れるようにしたらいいのでは。と思い立ちました。

できたもの

Linux

windows

使ったもの

https://tauri.app/

Electronと迷ったのですが作るものが自分用の簡単なリマインダーみたいなものでそんなに複雑ではなくどちらでも要件は満たせると判断しました。
それを踏まえてパッとDocを見てコードのイメージが沸いたtauriを採用しました。
ElectronもDocはかなり充実していたのですが、項目数が結構多くどこから手をつけてよいのか
今一わからなかった.....

tauriで開発してみてストレス感じることはなかったので正解だったと思います。

ざっとしたコード説明

https://github.com/k0i/tensyoku-draft-notifier

tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![manual_fetch_new_log])
        .setup(move |app| {
            ...
            // initial boot
            if is_initial_boot(&user_id_path) {
                // listening a user_id input event from frontend,  write it to file and restart the app to start scraping
                app.listen_global("input_user_id", move |event| {
                    if let Err(e) = File::create(&logs_path) {
                        error!("Failed to create logs file: {}", e);
                    };
                    if let Ok(mut f) = File::create(&user_id_path) {
                        if let Err(e) = write!(f, "{}", event.payload().unwrap_or("unknown")) {
                            error!("Failed to write user_id to file: {}", e);
                        };
                    } else {
                        error!("Failed to create user_id file");
                    };
                    restart(&h.env());
                });
                // early return because we can't start scraping without user_id
                return Ok(());
            }

初回起動だった場合、そもそもscrapingを始められないのでuser_idのinput eventを待ちます。
user_idをフロントから受け取ったらconfigファイルに書き込み、restartします。

tauri::async_runtime::spawn(async move {
                loop {
                    tokio::select! {
                       logs = rx.recv() => {
                          if let Some(new_logs) = logs {
                             let logs = match delta_update(new_logs,&logs_path){
Ok(l) => l,
Err(e) => {
    error!("Failed to update logs: {}", e);
    vec![]
                             }
                             };
                             if logs.is_empty() {
                                continue;
                             }
                             h.emit_all("fetch_new_log",FetchNewLogPayload{logs}).unwrap();
                          }
                       },
                       sig = &mut sigkill_rx => {
                           error!("sigkill received: {}", sig.unwrap());
                           process::exit(1);
                       }
                    }
                }
            });

scrapingを始めたら、channelで結果を受け取り、差分のみフロント側に通知します

useEffect(() => {
                ...
                let unlisten: UnlistenFn;
                async function fetchNewLog() {
                        unlisten = await listen("fetch_new_log", (event) => {
                                const { logs } = event.payload;
                                sendNotification({ title: "転職ドラフト", body: logs[0] });
                                setHistory((prev) => [...logs, ...prev]);
                                setNewLogs(logs);
                        });
                }
                fetchNewLog();
                return () => {
                        if (unlisten) {
                                unlisten();
                        }
                };
        }, []);

フロント側はbackendがらトラップ通知を受け取ったらnotificationを表示します。
何回も通知だされても大変なので、最初の1件をnotificationとして表示していますが、
差分が複数あったときにどうするかは今後リファクタリングを進めつつ考えていきたい所...

今後の展望

「転職ドラフト スクレイピング」で調べたところいくつか記事が出てきたので開発してみましたが、一息ついて規約を確認したらスクレイピングはがっつり禁止事項でした。

うまくできたらPEを配布してもみても面白いかもなぁと思っていたのですが、
規約に抵触するので個人の勉強用プログラムとしてゆっくりバグ取りやリファクタに
勤しもうと思います。

最後までよんでいただいてありがとうございました。

Discussion