イベント管理者のためのChrome拡張機能を作った話
最近ダッシュボード - connpassでイベントに出たり、主催したりすることが多くなったので、イベント統計情報に追加表示をする Chrome 拡張機能を作ったので実装の内容を紹介します。
↓ のように CVR(PV)と CVR(UU)を表示しています。(この画像の統計情報はダミーです)
- 拡張機能はこちらから追加できます
- リポジトリはこちら
プロジェクト構成
今回は popup 表示(chrome の右上をクリックして使うタイプの拡張機能)と、dom をいじる script を両方組み込む想定だったので、生意気にyarn workspace
のモノレポ構成にしています。
tree . -I node_modules -I out
.
├── assets # アイコンなどのファイル
│ ├── connpass_stats_cover.png
│ ├── icon_128x128.png
│ └── icon_stats.png
├── extensions
│ ├── manifest.json # Chrome Extensionの設定ファイル。
│ └── scripts
│ ├── index.js
│ └── index.js.map
├── popup/ # Next.jsのプロジェクト。現状は何もない。
├── scripts/ # DOM操作用のプロジェクト
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ └── setupTests.ts
│ └── tsconfig.json
├── renovate.json
├── package.json # workspaceの定義
├── README.md
└── yarn.lock
root においてある package.json はこんな感じ。
{
"name": "connpass-advanced-stats",
"version": "1.0.0",
"private": true,
"license": "MIT",
"workspaces": {
"packages": [
"popup",
"scripts"
],
"nohoist": [
"**"
]
},
"scripts": {
"dev": "concurrently -k \"yarn workspace popup dev\" ",
"lint": "concurrently -k \"yarn workspace popup lint\" ",
"test": "concurrently -k \"yarn workspace popup test\" ",
"build": "yarn build:prebuild && yarn build:main",
"build:main": "yarn workspace popup build && yarn workspace scripts build && cp -pr scripts/out/* extensions/scripts",
"build:prebuild": "rimraf extensions/scripts && mkdir -p extensions/scripts"
},
"devDependencies": {
"concurrently": "^7.6.0",
"rimraf": "^3.0.2 ",
"eslint": "^8.32.0",
"prettier": "^2.8.4",
"typescript": "^4.9.4"
}
}
popup
とscripts
に分けていて、タスクの実行にはconcurrently
を使っている。けどnpm-run-all
に変えたい、、、
基本的には各 package のビルドを行って、その生成物をextensions
フォルダに放り込んで、zip で圧縮して Google Web Store にアップロードします。
実装
Chrome 拡張機能ではmanifest.json
という設定ファイルで、どういう動作をさせるか、ユーザーにどういった権限を要求するかを指定します。
下記のようにcontent_scripts
のmatches
を指定すると、マッチした URL のときに指定した js ファイルが実行されるようになります。
{
"manifest_version": 3,
"name": "connpass advanced stats",
"description": "connpass advanced stats",
"version": "1.0",
"content_scripts": [
{
"matches": [
"https://connpass.com/event/*/stats/"
],
"js": [
"./scripts/index.js"
]
}
]
}
このcontent_scripts
で実行しているファイルは以下です。(直接 DOM 操作するの実は初めて書いた)
connpass のイベントの管理者になると、自分が管理者のイベントに対して、イベント統計ページが見れるようになりますが、その HTML からページビューや申込者数などの数値を取ってきて、割り算して並べて表示しているだけです。
// CVR書き込み
window.onload = function () {
const eventStatsElements = document.getElementsByClassName(
'EventStatsHero stats_hero_area flex-row'
);
const pageview =
+document.getElementsByClassName('PageviewsHero num')[0].innerHTML;
const visitor =
+document.getElementsByClassName('VisitorsHero num')[0].innerHTML;
const participation = +document.getElementsByClassName(
'ParticipationsHero num'
)[0].innerHTML;
const conversionRatePV = Math.floor((participation / pageview) * 1000) / 10;
const conversionRatePVElement = createConversionPVElement(conversionRatePV);
const conversionRateUU = Math.floor((participation / visitor) * 1000) / 10;
const conversionRateUUElement = createConversionUUElement(conversionRateUU);
eventStatsElements[0].appendChild(conversionRatePVElement);
eventStatsElements[0].appendChild(conversionRateUUElement);
};
const createConversionPVElement = (value: number) => {
const element = document.createElement('div');
element.innerHTML = `
<div class="list conversions">
<p>CVR(PV)</p>
<p class="ConversionHero num">${value}%</p>
</div>
`;
element.style.color = '#ff9900';
return element;
};
const createConversionUUElement = (value: number) => {
const element = document.createElement('div');
element.innerHTML = `
<div class="list conversions">
<p>CVR(UU)</p>
<p class="ConversionHero num">${value}%</p>
</div>
`;
element.style.color = '#0090c9';
return element;
};
リリース
renovate
で npm package のバージョンを自動更新したりしているので、こういった記事を参考にリリースを自動化しています。
name: release
on:
pull_request:
types:
- closed
branches:
- main
jobs:
prepare:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Cache node modules
id: cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: cache-node-modules-${{ hashFiles('yarn.lock') }}
- name: yarn install
run: |
yarn install
release:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Cache node modules
id: cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: cache-node-modules-${{ hashFiles('yarn.lock') }}
- name: yarn install
run: |
yarn install
- uses: fregante/daily-version-action@v2
id: daily-version
- name: build # NOTE: temporarily disable popup
run: |
yarn build
rm -rf ./extensions/popup
- name: Update manifest.json
run: |
npx dot-json@1 extensions/manifest.json version ${{ steps.daily-version.outputs.version }}
- name: Archive
run: |
zip -r extension.zip ./extensions
- name: Submit
run: |
npx chrome-webstore-upload-cli@2 upload --source extension.zip --auto-publish
env:
EXTENSION_ID: ${{ secrets.EXTENSION_ID }}
CLIENT_ID: ${{ secrets.CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }}
- name: Create release draft
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.daily-version.outputs.version }}
draft: false
generate_release_notes: true
files: extension.zip
version はfregante/daily-version-action@v2
を使って現在日付をもとに自動生成しています。
その version を元に tag を付与して、GitHub の release にも反映させるようにしています。
今後やりたいこと
- CVR のグラフ追加する
- イベント管理のページとかでも情報集計したりできそうなので、機能追加する
- connpass が公開している API 使うともうちょっと発展的な数値を取れそうなので、追加する
- scripts のビルドを高速化する(esbuild か vite か swc あたり?)
コントリビュートもお待ちしています 🙏
他の拡張機能もあります。
-
github-language-extension
- An extension that displays user statistics and ranking information for your language on the GitHub user page.
- GitHub Repository
- GitHub Language Stats - Chrome ウェブストア
-
line-message-validator
- LINE Messaging API のメッセージオブジェクトのバリデーションを行う拡張機能です。
- GitHub Repository
- LINE Message Validator - Chrome ウェブストア
Discussion