├── icons ├── icon_design.txt ├── icon128.png ├── icon48.png ├── icon_design.svg └── icon_design_improved.svg ├── icon.png ├── docs ├── image.png ├── chrome_webstore_registration_guide.md └── chrome_webstore_cicd_guide.md ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ ├── dependabot-auto-merge.yml │ ├── build.yml │ └── chrome-webstore-publish.yml ├── manifest.json ├── .eslintrc.json ├── SECURITY.md ├── background.js ├── LICENSE ├── package.json ├── webpack.config.js ├── README.md ├── popup.js ├── popup.html └── content.js /icons/icon_design.txt: -------------------------------------------------------------------------------- 1 | Creating icon design specifications 2 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GOROman/XKusoRepFilter/HEAD/icon.png -------------------------------------------------------------------------------- /docs/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GOROman/XKusoRepFilter/HEAD/docs/image.png -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GOROman/XKusoRepFilter/HEAD/icons/icon128.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GOROman/XKusoRepFilter/HEAD/icons/icon48.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ビルド成果物 2 | dist/ 3 | releases/ 4 | *.zip 5 | 6 | # 依存関係 7 | node_modules/ 8 | npm-debug.log 9 | yarn-debug.log 10 | yarn-error.log 11 | 12 | # OS固有のファイル 13 | .DS_Store 14 | Thumbs.db 15 | 16 | # エディタ固有のファイル 17 | .idea/ 18 | .vscode/ 19 | *.swp 20 | *.swo 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | open-pull-requests-limit: 10 8 | labels: 9 | - "dependencies" 10 | - "npm" 11 | commit-message: 12 | prefix: "npm" 13 | include: "scope" 14 | assignees: 15 | - "GOROman" -------------------------------------------------------------------------------- /icons/icon_design.svg: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "XKusoRepFilter", 4 | "version": "1.4.2", 5 | "description": "X(旧Twitter)で特定の文字列を含む投稿をブロックする拡張機能", 6 | "permissions": ["storage"], 7 | "action": { 8 | "default_popup": "popup.html", 9 | "default_icon": "icons/icon128.png" 10 | }, 11 | "content_scripts": [ 12 | { 13 | "matches": ["*://twitter.com/*", "*://x.com/*"], 14 | "js": ["content.js"] 15 | } 16 | ], 17 | "background": { 18 | "service_worker": "background.js" 19 | }, 20 | "icons": { 21 | "48": "icons/icon48.png", 22 | "128": "icons/icon128.png" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "webextensions": true, 6 | "node": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "indent": ["error", 2], 15 | "linebreak-style": ["error", "unix"], 16 | "quotes": ["error", "single"], 17 | "semi": ["error", "always"], 18 | "no-unused-vars": "warn", 19 | "no-console": ["warn", { "allow": ["warn", "error"] }], 20 | "camelcase": "warn" 21 | }, 22 | "globals": { 23 | "chrome": "readonly" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # セキュリティポリシー 2 | 3 | ## サポートバージョン 4 | 5 | 現在セキュリティアップデートがサポートされているバージョンは以下の通りです。 6 | 7 | | バージョン | サポート状況 | 8 | | ---------- | ------------------ | 9 | | 1.3.x | :white_check_mark: | 10 | | 1.2.x | :x: | 11 | | 1.1.x | :x: | 12 | | < 1.0 | :x: | 13 | 14 | ## 脆弱性の報告 15 | 16 | 脆弱性を発見した場合は、GitHubのIssueまたはPull Requestで報告してください。 17 | 18 | 報告された脆弱性は確認次第、できるだけ早く対応します。脆弱性が受理された場合は修正が行われ、新しいバージョンがリリースされます。 19 | 20 | ## セキュリティのベストプラクティス 21 | 22 | XKusoRepFilterは以下のセキュリティプラクティスに従っています: 23 | 24 | - ユーザーデータはローカルのChrome Storageにのみ保存され、外部サーバーには送信されません 25 | - 拡張機能は必要最小限の権限のみを要求します 26 | - コードは定期的に更新され、依存関係の脆弱性が修正されます 27 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // 拡張機能がインストールされた時に実行される 2 | chrome.runtime.onInstalled.addListener(function() { 3 | // デフォルト設定を保存 4 | chrome.storage.sync.get(['blockWords', 'showConfirmDialog', 'filterMode'], function(result) { 5 | let updates = {}; 6 | 7 | if (!result.blockWords) { 8 | updates.blockWords = 'しばらく観察していると'; 9 | } 10 | 11 | if (result.showConfirmDialog === undefined) { 12 | updates.showConfirmDialog = true; 13 | } 14 | 15 | if (result.filterMode === undefined) { 16 | updates.filterMode = 'block'; // デフォルトは「ブロックモード」 17 | } 18 | 19 | if (Object.keys(updates).length > 0) { 20 | chrome.storage.sync.set(updates); 21 | } 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /icons/icon_design_improved.svg: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 GOROman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xkusorepfilter", 3 | "version": "1.3.0", 4 | "description": "X(旧Twitter)で特定の文字列を含む投稿をブロックする拡張機能", 5 | "main": "background.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "package": "webpack && web-ext build -s dist/ -a releases/ --overwrite-dest", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/GOROman/XKusoRepFilter.git" 14 | }, 15 | "keywords": [ 16 | "chrome-extension", 17 | "twitter", 18 | "filter" 19 | ], 20 | "author": "GOROman", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/GOROman/XKusoRepFilter/issues" 24 | }, 25 | "homepage": "https://github.com/GOROman/XKusoRepFilter#readme", 26 | "devDependencies": { 27 | "@babel/core": "^7.23.9", 28 | "@babel/preset-env": "^7.23.9", 29 | "babel-loader": "^9.1.3", 30 | "clean-webpack-plugin": "^4.0.0", 31 | "copy-webpack-plugin": "^13.0.1", 32 | "web-ext": "^8.4.0", 33 | "webpack": "^5.99.6", 34 | "webpack-cli": "^6.0.1", 35 | "zip-webpack-plugin": "^4.0.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot Auto-Merge 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | 11 | jobs: 12 | dependabot-auto-merge: 13 | runs-on: ubuntu-latest 14 | if: ${{ github.actor == 'dependabot[bot]' }} 15 | 16 | steps: 17 | - name: Dependabot metadata 18 | id: metadata 19 | uses: dependabot/fetch-metadata@v2 20 | with: 21 | github-token: "${{ secrets.GITHUB_TOKEN }}" 22 | 23 | - name: Wait for build to succeed 24 | uses: lewagon/wait-on-check-action@v1.3.1 25 | with: 26 | ref: ${{ github.event.pull_request.head.sha }} 27 | check-name: 'build' 28 | repo-token: ${{ secrets.GITHUB_TOKEN }} 29 | wait-interval: 10 30 | 31 | - name: Enable auto-merge for Dependabot PRs 32 | if: ${{ steps.metadata.outputs.update-type != 'version-update:semver-major' }} 33 | run: gh pr merge --auto --merge "$PR_URL" 34 | env: 35 | PR_URL: ${{ github.event.pull_request.html_url }} 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const ZipPlugin = require('zip-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const CopyPlugin = require('copy-webpack-plugin'); 5 | const manifest = require('./manifest.json'); 6 | 7 | module.exports = { 8 | mode: 'production', 9 | entry: { 10 | background: './background.js', 11 | content: './content.js', 12 | popup: './popup.js' 13 | }, 14 | output: { 15 | path: path.resolve(__dirname, 'dist'), 16 | filename: '[name].js' 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.js$/, 22 | exclude: /node_modules/, 23 | use: { 24 | loader: 'babel-loader', 25 | options: { 26 | presets: ['@babel/preset-env'] 27 | } 28 | } 29 | } 30 | ] 31 | }, 32 | plugins: [ 33 | new CleanWebpackPlugin(), 34 | new CopyPlugin({ 35 | patterns: [ 36 | { from: 'manifest.json', to: '.' }, 37 | { from: 'popup.html', to: '.' }, 38 | { from: 'icon.png', to: '.' }, 39 | { from: 'docs/image.png', to: 'docs/image.png' }, 40 | { from: 'README.md', to: '.' } 41 | ], 42 | }), 43 | new ZipPlugin({ 44 | filename: `XKusoRepFilter-v${manifest.version}.zip` 45 | }) 46 | ] 47 | }; 48 | -------------------------------------------------------------------------------- /docs/chrome_webstore_registration_guide.md: -------------------------------------------------------------------------------- 1 | # Chrome ウェブストアへの登録ガイド 2 | 3 | このドキュメントでは、XKusoRepFilter拡張機能をChrome ウェブストアに登録する手順を説明します。 4 | 5 | ## 前提条件 6 | 7 | 1. Google アカウント 8 | 2. Chrome ウェブストア開発者アカウント(一度だけ$5の登録料が必要) 9 | 3. 拡張機能のZIPパッケージ 10 | 11 | ## 登録手順 12 | 13 | ### 1. 開発者アカウントの登録 14 | 15 | 1. [Chrome ウェブストア開発者ダッシュボード](https://chrome.google.com/webstore/devconsole)にアクセス 16 | 2. Googleアカウントでログイン 17 | 3. 開発者登録を完了し、$5の登録料を支払う 18 | 19 | ### 2. 拡張機能のパッケージ化 20 | 21 | 1. リポジトリをクローンまたはダウンロード 22 | 2. 必要なファイルのみを含むZIPファイルを作成 23 | ``` 24 | zip -r xkusorepfilter.zip * -x "*.git*" -x "node_modules/*" -x "docs/*" -x "*.md" -x "package*" -x "webpack*" 25 | ``` 26 | 27 | ### 3. 拡張機能のアップロード 28 | 29 | 1. [Chrome ウェブストア開発者ダッシュボード](https://chrome.google.com/webstore/devconsole)にアクセス 30 | 2. 「新しいアイテムを追加」ボタンをクリック 31 | 3. 作成したZIPファイルを選択してアップロード 32 | 33 | ### 4. 拡張機能の情報を入力 34 | 35 | #### ストア掲載情報 36 | 37 | 1. **言語と地域**: 日本語を選択 38 | 2. **拡張機能名**: XKusoRepFilter 39 | 3. **簡単な説明**: X(旧Twitter)で特定の文字列を含む投稿をブロックする拡張機能 40 | 4. **詳細な説明**: 41 | ``` 42 | XKusoRepFilterは、X(旧Twitter)上で特定の文字列を含む投稿を自動的にブロックまたは表示するための拡張機能です。 43 | 44 | 主な機能: 45 | - 特定のフレーズを含む投稿を非表示にする 46 | - または特定のフレーズを含む投稿のみを表示する 47 | - カスタマイズ可能なブロックワードリスト 48 | - 確認ダイアログのオプション 49 | - 認証済みアカウントやフォロワーの投稿は保護 50 | 51 | デフォルトでは「しばらく観察していると」「紹介したこのブロガー」「彼の指導のもと」などの投資スパム関連のフレーズをブロックします。 52 | ``` 53 | 5. **スクリーンショット**: 拡張機能の使用例のスクリーンショットを追加(1280x800px推奨) 54 | 6. **プロモーション画像**: 必要に応じて追加(可選) 55 | 7. **アイコン**: すでに準備済み(128x128px) 56 | 57 | #### プライバシー 58 | 59 | 1. **単一目的**: 「X(旧Twitter)上で特定の文字列を含む投稿をフィルタリングする」 60 | 2. **権限の説明**: ストレージ権限はユーザー設定の保存に使用 61 | 3. **データ収集**: 「この拡張機能はユーザーデータを収集しません」を選択 62 | 63 | #### 配布 64 | 65 | 1. **可視性**: 公開(Chrome ウェブストア全体で公開) 66 | 2. **国/地域**: すべての国/地域で公開 67 | 68 | ### 5. 審査のために提出 69 | 70 | 1. すべての情報を入力後、「審査のために提出」ボタンをクリック 71 | 2. 審査には数日から数週間かかる場合があります 72 | 3. 審査が完了すると、拡張機能がChrome ウェブストアに公開されます 73 | 74 | ## 注意事項 75 | 76 | - 審査プロセスでは、拡張機能のセキュリティとプライバシーポリシーが厳しくチェックされます 77 | - 拡張機能のマニフェストがManifest V3に準拠していることを確認してください 78 | - 拡張機能のアイコンとスクリーンショットは高品質であることが重要です 79 | - 説明文は明確で、拡張機能の機能を正確に表現するようにしてください 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XKusoRepFilter 2 | 3 |  4 |  5 |  6 | 7 | X(旧Twitter)で特定の文字列「しばらく観察していると」などを含む投稿をブロックするChrome拡張機能です。 8 | 9 |  10 | 11 | ## 機能 12 | 13 | - X(旧Twitter)のタイムラインで、特定の文字列を含む投稿を非表示にします 14 | - ブロックする文字列は設定画面でカスタマイズ可能 15 | - デフォルトでは「しばらく観察していると」という文字列を含む投稿をブロック 16 | - ブロック前に確認ダイアログを表示するオプション(デフォルトでON) 17 | - 自分の投稿とフォロワーの投稿はブロックされません 18 | - 認証済みアカウント(青・黄色・グレーのチェックマーク)の投稿はブロックされません 19 | - 自分やフォロワーの投稿に特定の文字列が含まれる場合、背景黄色・文字赤でハイライト表示されます 20 | - ダークモード対応のモダンな設定画面 21 | 22 | ## インストール方法 23 | 24 | ### リリースからインストール 25 | 26 | 1. [Releasesページ](https://github.com/GOROman/XKusoRepFilter/releases)から最新の拡張機能パッケージ(.zip)をダウンロード 27 | 2. ファイルを解凍 28 | 3. Chromeで `chrome://extensions/` を開く 29 | 4. 右上の「デベロッパーモード」をオンにする 30 | 5. 「パッケージ化されていない拡張機能を読み込む」をクリック 31 | 6. 解凍したフォルダを選択 32 | 33 | ### ソースからインストール 34 | 35 | 1. このリポジトリをクローン 36 | ``` 37 | git clone https://github.com/GOROman/XKusoRepFilter.git 38 | ``` 39 | 2. 依存関係をインストール 40 | ``` 41 | npm install 42 | ``` 43 | 3. 拡張機能をビルド 44 | ``` 45 | npm run build 46 | ``` 47 | 4. Chromeで `chrome://extensions/` を開く 48 | 5. 右上の「デベロッパーモード」をオンにする 49 | 6. 「パッケージ化されていない拡張機能を読み込む」をクリック 50 | 7. `dist` フォルダを選択 51 | 52 | ## 使い方 53 | 54 | 1. インストール後、Chromeツールバーの拡張機能アイコンをクリック 55 | 2. 「XKusoRepFilter」アイコンをクリック 56 | 3. ブロックしたい文字列を入力(1行に1つ) 57 | 4. 「保存」ボタンをクリック 58 | 5. X(Twitter)のページを更新すると設定が反映されます 59 | 60 | ## 開発者向け情報 61 | 62 | ### CI/CDパイプライン 63 | 64 | このプロジェクトはGitHub Actionsを使用して自動ビルドとリリースを行っています。 65 | 66 | - `main`ブランチへのプッシュ時に自動ビルドが実行されます 67 | - タグ(`v*`形式)を付けてプッシュすると、自動的にリリースが作成されます 68 | 69 | リリースを作成するには、以下のコマンドを実行します(例:バージョン1.3.0の場合): 70 | 71 | ```bash 72 | git tag -a v1.3.0 -m "v1.3.0リリース" 73 | git push origin v1.3.0 74 | ``` 75 | 76 | ### ローカル開発 77 | 78 | ```bash 79 | # 依存関係のインストール 80 | npm install 81 | 82 | # 開発ビルド 83 | npm run build 84 | 85 | # パッケージ作成 86 | npm run package 87 | ``` 88 | 89 | ## 更新履歴 90 | 91 | ### v1.4.1 92 | - 認証済みアカウント(青・黄色・グレーのチェックマーク)の検出機能を修正 93 | - X(旧Twitter)のUI変更に対応 94 | 95 | ### v1.4.0 96 | - 初期リリース 97 | 98 | _❤️❤️❤️ナル先生 参上❤️❤️❤️_ 99 | 100 | -------------------------------------------------------------------------------- /docs/chrome_webstore_cicd_guide.md: -------------------------------------------------------------------------------- 1 | # Chrome ウェブストア公開用CI/CDガイド 2 | 3 | このドキュメントでは、GitHub Actionsを使用してXKusoRepFilter拡張機能をChrome ウェブストアに自動的に公開する方法について説明します。 4 | 5 | ## 概要 6 | 7 | このCI/CDワークフローは以下の機能を提供します: 8 | 9 | 1. 拡張機能のビルドと最適化 10 | 2. Chrome ウェブストア用のパッケージ作成 11 | 3. Chrome ウェブストアへの自動公開 12 | 4. リリースノートの管理 13 | 14 | ## 前提条件 15 | 16 | 1. Chrome ウェブストア開発者アカウント 17 | 2. Chrome ウェブストアAPIアクセス用の認証情報 18 | 3. GitHubリポジトリへの適切な権限 19 | 20 | ## 必要なシークレット 21 | 22 | 以下のシークレットをGitHubリポジトリに設定する必要があります: 23 | 24 | 1. `EXTENSION_ID` - Chrome ウェブストアの拡張機能ID 25 | 2. `CLIENT_ID` - Google OAuth クライアントID 26 | 3. `CLIENT_SECRET` - Google OAuth クライアントシークレット 27 | 4. `REFRESH_TOKEN` - Google OAuth リフレッシュトークン 28 | 29 | ### シークレットの設定方法 30 | 31 | 1. GitHubリポジトリの「Settings」タブを開く 32 | 2. 左側のメニューから「Secrets and variables」→「Actions」を選択 33 | 3. 「New repository secret」ボタンをクリックして各シークレットを追加 34 | 35 | ## 認証情報の取得方法 36 | 37 | ### 拡張機能IDの取得 38 | 39 | 1. 拡張機能をChrome ウェブストアに初回登録する 40 | 2. 登録後、ダッシュボードに表示される拡張機能IDをコピー 41 | 42 | ### Google OAuth認証情報の取得 43 | 44 | 1. [Google Cloud Console](https://console.cloud.google.com/)にアクセス 45 | 2. 新しいプロジェクトを作成 46 | 3. 「APIとサービス」→「認証情報」を選択 47 | 4. 「認証情報を作成」→「OAuthクライアントID」を選択 48 | 5. アプリケーションタイプとして「デスクトップアプリ」を選択 49 | 6. クライアントIDとクライアントシークレットを取得 50 | 51 | ### リフレッシュトークンの取得 52 | 53 | 以下のコマンドを使用してリフレッシュトークンを取得します: 54 | 55 | ```bash 56 | # 必要なパッケージのインストール 57 | npm install -g chrome-webstore-upload-cli 58 | 59 | # リフレッシュトークンの取得 60 | chrome-webstore-upload-cli refresh-token --client-id=YOUR_CLIENT_ID --client-secret=YOUR_CLIENT_SECRET 61 | ``` 62 | 63 | 表示される指示に従ってGoogleアカウントで認証し、リフレッシュトークンを取得します。 64 | 65 | ## ワークフローの使用方法 66 | 67 | ### 手動トリガー 68 | 69 | 1. GitHubリポジトリの「Actions」タブを開く 70 | 2. 「Chrome ウェブストア公開」ワークフローを選択 71 | 3. 「Run workflow」ボタンをクリック 72 | 4. 以下の情報を入力: 73 | - バージョン:拡張機能の新しいバージョン(例:1.4.3) 74 | - リリースノート:変更内容の説明 75 | 5. 「Run workflow」ボタンをクリックして実行 76 | 77 | ### 自動トリガー 78 | 79 | このワークフローは、GitHubでリリースが公開されたときにも自動的に実行されます: 80 | 81 | 1. GitHubリポジトリの「Releases」タブを開く 82 | 2. 「Draft a new release」ボタンをクリック 83 | 3. タグバージョンとリリースタイトルを入力 84 | 4. リリースノートを記入 85 | 5. 「Publish release」ボタンをクリック 86 | 87 | リリースが公開されると、ワークフローが自動的に実行され、Chrome ウェブストアに拡張機能が公開されます。 88 | 89 | ## ワークフローの詳細 90 | 91 | ### ビルドジョブ 92 | 93 | 1. リポジトリのチェックアウト 94 | 2. Node.jsのセットアップ 95 | 3. 依存関係のインストール 96 | 4. マニフェストのバージョン更新(手動トリガーの場合) 97 | 5. webpackの設定ファイル作成 98 | 6. 拡張機能のビルド 99 | 7. Chrome ウェブストア用ZIPファイルの作成 100 | 8. 成果物のアップロード 101 | 102 | ### 公開ジョブ 103 | 104 | 1. ビルド成果物のダウンロード 105 | 2. Chrome ウェブストアへの公開 106 | 3. リリースノートの作成 107 | 4. 公開完了通知 108 | 109 | ## トラブルシューティング 110 | 111 | ### 公開に失敗する場合 112 | 113 | 1. GitHubのActionsタブでワークフローの実行ログを確認 114 | 2. シークレットが正しく設定されているか確認 115 | 3. Chrome ウェブストアのダッシュボードで拡張機能のステータスを確認 116 | 117 | ### 認証エラーが発生する場合 118 | 119 | 1. リフレッシュトークンの有効期限が切れていないか確認 120 | 2. 必要に応じて新しいリフレッシュトークンを生成 121 | 3. GitHubのシークレットを更新 122 | 123 | ## 注意事項 124 | 125 | - Chrome ウェブストアの審査には時間がかかる場合があります 126 | - 公開後も審査が必要なため、すぐにユーザーに配信されるわけではありません 127 | - マニフェストのバージョンは必ず前回より大きな値にしてください 128 | - リリースノートは明確で詳細なものを記載してください 129 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: ビルドとリリース 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | tags: 7 | - 'v*' 8 | pull_request: 9 | branches: [ main ] 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Node.jsのセットアップ 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: '20' 22 | cache: 'npm' 23 | 24 | - name: 依存関係のインストール 25 | run: | 26 | npm init -y 27 | npm install --save-dev eslint@8.57.0 28 | 29 | - name: 静的解析の実行 30 | run: | 31 | echo '開始:JavaScriptファイルの静的解析' 32 | npx eslint . --ext .js 33 | 34 | build: 35 | needs: lint 36 | runs-on: ubuntu-latest 37 | 38 | steps: 39 | - uses: actions/checkout@v4 40 | 41 | - name: Node.jsのセットアップ 42 | uses: actions/setup-node@v4 43 | with: 44 | node-version: '20' 45 | cache: 'npm' 46 | 47 | - name: 依存関係のインストール 48 | run: | 49 | npm init -y 50 | npm install --save-dev @babel/core @babel/preset-env babel-loader webpack webpack-cli zip-webpack-plugin clean-webpack-plugin copy-webpack-plugin eslint@8.57.0 51 | 52 | - name: webpackの設定ファイル作成 53 | run: | 54 | cat > webpack.config.js << 'EOL' 55 | const path = require('path'); 56 | const ZipPlugin = require('zip-webpack-plugin'); 57 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 58 | const CopyPlugin = require('copy-webpack-plugin'); 59 | const package = require('./package.json'); 60 | const manifest = require('./manifest.json'); 61 | 62 | module.exports = { 63 | mode: 'production', 64 | entry: { 65 | background: './background.js', 66 | content: './content.js', 67 | popup: './popup.js' 68 | }, 69 | output: { 70 | path: path.resolve(__dirname, 'dist'), 71 | filename: '[name].js' 72 | }, 73 | module: { 74 | rules: [ 75 | { 76 | test: /\.js$/, 77 | exclude: /node_modules/, 78 | use: { 79 | loader: 'babel-loader', 80 | options: { 81 | presets: ['@babel/preset-env'] 82 | } 83 | } 84 | } 85 | ] 86 | }, 87 | plugins: [ 88 | new CleanWebpackPlugin(), 89 | new CopyPlugin({ 90 | patterns: [ 91 | { from: 'manifest.json', to: '.' }, 92 | { from: 'popup.html', to: '.' }, 93 | { from: 'icon.png', to: '.' }, 94 | { from: 'docs/image.png', to: 'docs/image.png' } 95 | ], 96 | }), 97 | new ZipPlugin({ 98 | filename: `XKusoRepFilter-v${manifest.version}.zip` 99 | }) 100 | ] 101 | }; 102 | EOL 103 | 104 | - name: ビルド 105 | run: npx webpack 106 | 107 | - name: 成果物のアップロード 108 | uses: actions/upload-artifact@v4 109 | with: 110 | name: extension-package 111 | path: dist/*.zip 112 | 113 | release: 114 | needs: build 115 | if: startsWith(github.ref, 'refs/tags/') 116 | runs-on: ubuntu-latest 117 | permissions: 118 | contents: write 119 | 120 | steps: 121 | - name: 成果物のダウンロード 122 | uses: actions/download-artifact@v4 123 | with: 124 | name: extension-package 125 | path: ./ 126 | 127 | - name: リリースの作成 128 | uses: softprops/action-gh-release@v1 129 | with: 130 | files: ./*.zip 131 | draft: false 132 | prerelease: false 133 | env: 134 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 135 | -------------------------------------------------------------------------------- /.github/workflows/chrome-webstore-publish.yml: -------------------------------------------------------------------------------- 1 | name: Chrome ウェブストア公開 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: '拡張機能のバージョン(例:1.4.3)' 8 | required: true 9 | release_notes: 10 | description: 'リリースノート' 11 | required: true 12 | release: 13 | types: [published] 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Node.jsのセットアップ 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: '20' 25 | cache: 'npm' 26 | 27 | - name: 依存関係のインストール 28 | run: | 29 | npm init -y 30 | npm install --save-dev @babel/core @babel/preset-env babel-loader webpack webpack-cli zip-webpack-plugin clean-webpack-plugin copy-webpack-plugin eslint@8.57.0 31 | 32 | - name: マニフェストのバージョン更新 33 | if: github.event_name == 'workflow_dispatch' 34 | run: | 35 | # jqを使用してmanifest.jsonのバージョンを更新 36 | jq '.version = "${{ github.event.inputs.version }}"' manifest.json > manifest.json.tmp 37 | mv manifest.json.tmp manifest.json 38 | 39 | - name: webpackの設定ファイル作成 40 | run: | 41 | cat > webpack.config.js << 'EOL' 42 | const path = require('path'); 43 | const ZipPlugin = require('zip-webpack-plugin'); 44 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 45 | const CopyPlugin = require('copy-webpack-plugin'); 46 | const manifest = require('./manifest.json'); 47 | 48 | module.exports = { 49 | mode: 'production', 50 | entry: { 51 | background: './background.js', 52 | content: './content.js', 53 | popup: './popup.js' 54 | }, 55 | output: { 56 | path: path.resolve(__dirname, 'dist'), 57 | filename: '[name].js' 58 | }, 59 | module: { 60 | rules: [ 61 | { 62 | test: /\.js$/, 63 | exclude: /node_modules/, 64 | use: { 65 | loader: 'babel-loader', 66 | options: { 67 | presets: ['@babel/preset-env'] 68 | } 69 | } 70 | } 71 | ] 72 | }, 73 | plugins: [ 74 | new CleanWebpackPlugin(), 75 | new CopyPlugin({ 76 | patterns: [ 77 | { from: 'manifest.json', to: '.' }, 78 | { from: 'popup.html', to: '.' }, 79 | { from: 'icons', to: 'icons' }, 80 | { from: 'icon.png', to: '.' } 81 | ], 82 | }), 83 | new ZipPlugin({ 84 | filename: `XKusoRepFilter-v${manifest.version}.zip` 85 | }) 86 | ] 87 | }; 88 | EOL 89 | 90 | - name: ビルド 91 | run: npx webpack 92 | 93 | - name: Chrome ウェブストア用ZIPファイル作成 94 | run: | 95 | cd dist 96 | zip -r chrome-store-package.zip * 97 | ls -la 98 | 99 | - name: 成果物のアップロード 100 | uses: actions/upload-artifact@v4 101 | with: 102 | name: chrome-webstore-package 103 | path: dist/chrome-store-package.zip 104 | 105 | publish: 106 | needs: build 107 | runs-on: ubuntu-latest 108 | steps: 109 | - name: 成果物のダウンロード 110 | uses: actions/download-artifact@v4 111 | with: 112 | name: chrome-webstore-package 113 | path: ./ 114 | 115 | - name: Chrome ウェブストアに公開 116 | uses: mnao305/chrome-extension-upload@v4.0.1 117 | with: 118 | file-path: chrome-store-package.zip 119 | extension-id: ${{ secrets.EXTENSION_ID }} 120 | client-id: ${{ secrets.CLIENT_ID }} 121 | client-secret: ${{ secrets.CLIENT_SECRET }} 122 | refresh-token: ${{ secrets.REFRESH_TOKEN }} 123 | publish: true 124 | publish-target: default 125 | 126 | - name: リリースノートの作成 127 | if: github.event_name == 'workflow_dispatch' 128 | run: | 129 | echo "${{ github.event.inputs.release_notes }}" > release_notes.txt 130 | 131 | - name: リリースノートの取得 132 | if: github.event_name == 'release' 133 | run: | 134 | echo "${{ github.event.release.body }}" > release_notes.txt 135 | 136 | - name: 公開完了通知 137 | run: | 138 | echo "Chrome ウェブストアへの公開が完了しました" 139 | echo "バージョン: ${{ github.event.inputs.version || github.event.release.tag_name }}" 140 | echo "リリースノート:" 141 | cat release_notes.txt 142 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | // デフォルト値を設定 3 | const defaultBlockWords = 'しばらく観察していると\n紹介したこのブロガー\n彼の指導のもと'; 4 | const defaultShowConfirmDialog = true; 5 | const defaultFilterMode = 'block'; 6 | 7 | 8 | // UI要素 9 | const blockWordsTextarea = document.getElementById('blockWords'); 10 | const showConfirmDialogCheckbox = document.getElementById('showConfirmDialog'); 11 | const blockModeRadio = document.getElementById('blockMode'); 12 | const showOnlyModeRadio = document.getElementById('showOnlyMode'); 13 | 14 | const saveButton = document.getElementById('saveButton'); 15 | const statusMessage = document.getElementById('status'); 16 | const formGroups = document.querySelectorAll('.form-group'); 17 | 18 | // フォームグループにアニメーション効果を追加 19 | formGroups.forEach((group, index) => { 20 | group.style.opacity = '0'; 21 | group.style.transform = 'translateY(10px)'; 22 | 23 | setTimeout(() => { 24 | group.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; 25 | group.style.opacity = '1'; 26 | group.style.transform = 'translateY(0)'; 27 | }, 100 * (index + 1)); 28 | }); 29 | 30 | // 保存されている設定を読み込む 31 | chrome.storage.sync.get(['blockWords', 'showConfirmDialog', 'filterMode'], function(result) { 32 | const blockWords = result.blockWords || defaultBlockWords; 33 | blockWordsTextarea.value = blockWords; 34 | 35 | const showConfirmDialog = result.showConfirmDialog !== undefined ? result.showConfirmDialog : defaultShowConfirmDialog; 36 | showConfirmDialogCheckbox.checked = showConfirmDialog; 37 | 38 | const filterMode = result.filterMode || defaultFilterMode; 39 | if (filterMode === 'block') { 40 | blockModeRadio.checked = true; 41 | } else if (filterMode === 'showOnly') { 42 | showOnlyModeRadio.checked = true; 43 | } 44 | 45 | 46 | 47 | // テキストエリアにフォーカスアニメーション 48 | blockWordsTextarea.addEventListener('focus', function() { 49 | this.parentElement.style.transform = 'scale(1.01)'; 50 | }); 51 | 52 | blockWordsTextarea.addEventListener('blur', function() { 53 | this.parentElement.style.transform = 'scale(1)'; 54 | }); 55 | }); 56 | 57 | // 保存ボタンのクリックイベント 58 | saveButton.addEventListener('click', function() { 59 | // ボタンにクリックエフェクト 60 | this.classList.add('clicked'); 61 | 62 | // ボタンのテキストを変更 63 | const originalText = this.textContent; 64 | this.textContent = '保存中...'; 65 | 66 | const blockWords = blockWordsTextarea.value; 67 | const showConfirmDialog = showConfirmDialogCheckbox.checked; 68 | const filterMode = blockModeRadio.checked ? 'block' : 'showOnly'; 69 | 70 | 71 | // 設定を保存 72 | chrome.storage.sync.set({ 73 | blockWords: blockWords, 74 | showConfirmDialog: showConfirmDialog, 75 | filterMode: filterMode 76 | }, function() { 77 | // 保存完了メッセージを表示 78 | statusMessage.style.display = 'block'; 79 | statusMessage.style.opacity = '0'; 80 | statusMessage.style.transform = 'translateY(10px)'; 81 | 82 | setTimeout(() => { 83 | statusMessage.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; 84 | statusMessage.style.opacity = '1'; 85 | statusMessage.style.transform = 'translateY(0)'; 86 | }, 10); 87 | 88 | // ボタンのテキストを元に戻す 89 | saveButton.textContent = '保存完了!'; 90 | 91 | setTimeout(function() { 92 | saveButton.textContent = originalText; 93 | saveButton.classList.remove('clicked'); 94 | statusMessage.style.opacity = '0'; 95 | statusMessage.style.transform = 'translateY(10px)'; 96 | 97 | setTimeout(() => { 98 | statusMessage.style.display = 'none'; 99 | }, 300); 100 | }, 2000); 101 | }); 102 | }); 103 | 104 | // バージョン情報を表示 105 | const manifestData = chrome.runtime.getManifest(); 106 | const versionElement = document.getElementById('version'); 107 | if (versionElement && manifestData.version) { 108 | versionElement.textContent = `XKusoRepFilter v${manifestData.version}`; 109 | 110 | // バージョン表示にアニメーション効果を追加 111 | versionElement.style.opacity = '0'; 112 | versionElement.style.transform = 'translateY(5px)'; 113 | 114 | setTimeout(() => { 115 | versionElement.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; 116 | versionElement.style.opacity = '1'; 117 | versionElement.style.transform = 'translateY(0)'; 118 | }, 500); 119 | 120 | // クリックでバージョン情報のアニメーション 121 | versionElement.addEventListener('click', function() { 122 | this.style.transform = 'scale(1.1)'; 123 | setTimeout(() => { 124 | this.style.transform = 'scale(1)'; 125 | }, 300); 126 | }); 127 | } 128 | }); 129 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |