diff --git a/.config/docker_example.env b/.config/docker_example.env index 7a0261524b..4fe8e76b78 100644 --- a/.config/docker_example.env +++ b/.config/docker_example.env @@ -2,3 +2,4 @@ POSTGRES_PASSWORD=example-misskey-pass POSTGRES_USER=example-misskey-user POSTGRES_DB=misskey +DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c5755315fc..d4678ec5e0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,7 +17,7 @@ updates: directory: "/" schedule: interval: daily - open-pull-requests-limit: 5 + open-pull-requests-limit: 10 # List dependencies required to be updated together, sharing the same version numbers. # Those who simply have the common owner (e.g. @fastify) don't need to be listed. groups: diff --git a/CHANGELOG.md b/CHANGELOG.md index eedd705a58..5b0a923b70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,11 @@ ## 202x.x.x (Unreleased) +### General +- Feat: [mCaptcha](https://github.com/mCaptcha/mCaptcha)のサポートを追加 + ### Client +- Feat: 新しいゲームを追加 - Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように - Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正 - Enhance: チャンネルノートのピン留めをノートのメニューからできるよ diff --git a/docker-compose_example.yml b/docker-compose_example.yml index 60ba4dc8ca..5cebbe4164 100644 --- a/docker-compose_example.yml +++ b/docker-compose_example.yml @@ -7,6 +7,7 @@ services: links: - db - redis +# - mcaptcha # - meilisearch depends_on: db: @@ -48,6 +49,36 @@ services: interval: 5s retries: 20 +# mcaptcha: +# restart: always +# image: mcaptcha/mcaptcha:latest +# networks: +# internal_network: +# external_network: +# aliases: +# - localhost +# ports: +# - 7493:7493 +# env_file: +# - .config/docker.env +# environment: +# PORT: 7493 +# MCAPTCHA_redis_URL: "redis://mcaptcha_redis/" +# depends_on: +# db: +# condition: service_healthy +# mcaptcha_redis: +# condition: service_healthy +# +# mcaptcha_redis: +# image: mcaptcha/cache:latest +# networks: +# - internal_network +# healthcheck: +# test: "redis-cli ping" +# interval: 5s +# retries: 20 + # meilisearch: # restart: always # image: getmeili/meilisearch:v1.3.4 diff --git a/locales/index.d.ts b/locales/index.d.ts index 8a97635215..15e3069925 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -402,6 +402,11 @@ export interface Locale { "enableHcaptcha": string; "hcaptchaSiteKey": string; "hcaptchaSecretKey": string; + "mcaptcha": string; + "enableMcaptcha": string; + "mcaptchaSiteKey": string; + "mcaptchaSecretKey": string; + "mcaptchaInstanceUrl": string; "recaptcha": string; "enableRecaptcha": string; "recaptchaSiteKey": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index daa777b08e..090b020c4d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -399,6 +399,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptchaを有効にする" hcaptchaSiteKey: "サイトキー" hcaptchaSecretKey: "シークレットキー" +mcaptcha: "mCaptcha" +enableMcaptcha: "mCaptchaを有効にする" +mcaptchaSiteKey: "サイトキー" +mcaptchaSecretKey: "シークレットキー" +mcaptchaInstanceUrl: "mCaptchaのインスタンスのURL" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHAを有効にする" recaptchaSiteKey: "サイトキー" diff --git a/packages/backend/migration/1704373210054-support-mcaptcha.js b/packages/backend/migration/1704373210054-support-mcaptcha.js new file mode 100644 index 0000000000..ce42b90716 --- /dev/null +++ b/packages/backend/migration/1704373210054-support-mcaptcha.js @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class SupportMcaptcha1704373210054 { + name = 'SupportMcaptcha1704373210054' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "enableMcaptcha" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "mcaptchaSitekey" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "mcaptchaSecretKey" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "mcaptchaInstanceUrl" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mcaptchaInstanceUrl"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mcaptchaSecretKey"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mcaptchaSitekey"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableMcaptcha"`); + } +} diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index f64196f4fc..6c5ee4835d 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -73,6 +73,37 @@ export class CaptchaService { } } + // https://codeberg.org/Gusted/mCaptcha/src/branch/main/mcaptcha.go + @bindThis + public async verifyMcaptcha(secret: string, siteKey: string, instanceHost: string, response: string | null | undefined): Promise<void> { + if (response == null) { + throw new Error('mcaptcha-failed: no response provided'); + } + + const endpointUrl = new URL('/api/v1/pow/siteverify', instanceHost); + const result = await this.httpRequestService.send(endpointUrl.toString(), { + method: 'POST', + body: JSON.stringify({ + key: siteKey, + secret: secret, + token: response, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (result.status !== 200) { + throw new Error('mcaptcha-failed: mcaptcha didn\'t return 200 OK'); + } + + const resp = (await result.json()) as { valid: boolean }; + + if (!resp.valid) { + throw new Error('mcaptcha-request-failed'); + } + } + @bindThis public async verifyTurnstile(secret: string, response: string | null | undefined): Promise<void> { if (response == null) { diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 06d2f73891..c9b4419715 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -191,6 +191,29 @@ export class MiMeta { }) public hcaptchaSecretKey: string | null; + @Column('boolean', { + default: false, + }) + public enableMcaptcha: boolean; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public mcaptchaSitekey: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public mcaptchaSecretKey: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public mcaptchaInstanceUrl: string | null; + @Column('boolean', { default: false, }) @@ -482,7 +505,7 @@ export class MiMeta { nullable: true, }) public truemailInstance: string | null; - + @Column('varchar', { length: 1024, nullable: true, diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 753984ef52..6b4d9d9f70 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -65,6 +65,7 @@ export class SignupApiService { 'hcaptcha-response'?: string; 'g-recaptcha-response'?: string; 'turnstile-response'?: string; + 'm-captcha-response'?: string; } }>, reply: FastifyReply, @@ -82,6 +83,12 @@ export class SignupApiService { }); } + if (instance.enableMcaptcha && instance.mcaptchaSecretKey && instance.mcaptchaSitekey && instance.mcaptchaInstanceUrl) { + await this.captchaService.verifyMcaptcha(instance.mcaptchaSecretKey, instance.mcaptchaSitekey, instance.mcaptchaInstanceUrl, body['m-captcha-response']).catch(err => { + throw new FastifyReplyError(400, err); + }); + } + if (instance.enableRecaptcha && instance.recaptchaSecretKey) { await this.captchaService.verifyRecaptcha(instance.recaptchaSecretKey, body['g-recaptcha-response']).catch(err => { throw new FastifyReplyError(400, err); diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 26626c54b5..a31cbf531a 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -41,6 +41,18 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + enableMcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + mcaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + mcaptchaInstanceUrl: { + type: 'string', + optional: false, nullable: true, + }, enableRecaptcha: { type: 'boolean', optional: false, nullable: false, @@ -163,6 +175,10 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + mcaptchaSecretKey: { + type: 'string', + optional: false, nullable: true, + }, recaptchaSecretKey: { type: 'string', optional: false, nullable: true, @@ -472,6 +488,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- emailRequiredForSignup: instance.emailRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, + enableMcaptcha: instance.enableMcaptcha, + mcaptchaSiteKey: instance.mcaptchaSitekey, + mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl, enableRecaptcha: instance.enableRecaptcha, recaptchaSiteKey: instance.recaptchaSiteKey, enableTurnstile: instance.enableTurnstile, @@ -503,6 +522,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- sensitiveWords: instance.sensitiveWords, preservedUsernames: instance.preservedUsernames, hcaptchaSecretKey: instance.hcaptchaSecretKey, + mcaptchaSecretKey: instance.mcaptchaSecretKey, recaptchaSecretKey: instance.recaptchaSecretKey, turnstileSecretKey: instance.turnstileSecretKey, sensitiveMediaDetection: instance.sensitiveMediaDetection, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 633bd0ba52..eb5b59221c 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -63,6 +63,10 @@ export const paramDef = { enableHcaptcha: { type: 'boolean' }, hcaptchaSiteKey: { type: 'string', nullable: true }, hcaptchaSecretKey: { type: 'string', nullable: true }, + enableMcaptcha: { type: 'boolean' }, + mcaptchaSiteKey: { type: 'string', nullable: true }, + mcaptchaInstanceUrl: { type: 'string', nullable: true }, + mcaptchaSecretKey: { type: 'string', nullable: true }, enableRecaptcha: { type: 'boolean' }, recaptchaSiteKey: { type: 'string', nullable: true }, recaptchaSecretKey: { type: 'string', nullable: true }, @@ -284,6 +288,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.hcaptchaSecretKey = ps.hcaptchaSecretKey; } + if (ps.enableMcaptcha !== undefined) { + set.enableMcaptcha = ps.enableMcaptcha; + } + + if (ps.mcaptchaSiteKey !== undefined) { + set.mcaptchaSitekey = ps.mcaptchaSiteKey; + } + + if (ps.mcaptchaInstanceUrl !== undefined) { + set.mcaptchaInstanceUrl = ps.mcaptchaInstanceUrl; + } + + if (ps.mcaptchaSecretKey !== undefined) { + set.mcaptchaSecretKey = ps.mcaptchaSecretKey; + } + if (ps.enableRecaptcha !== undefined) { set.enableRecaptcha = ps.enableRecaptcha; } @@ -487,7 +507,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.verifymailAuthKey = ps.verifymailAuthKey; } } - + if (ps.enableTruemailApi !== undefined) { set.enableTruemailApi = ps.enableTruemailApi; } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index f7c2962bc2..529e82678d 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -108,6 +108,18 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + enableMcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + mcaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + mcaptchaInstanceUrl: { + type: 'string', + optional: false, nullable: true, + }, enableRecaptcha: { type: 'boolean', optional: false, nullable: false, @@ -351,6 +363,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- emailRequiredForSignup: instance.emailRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, + enableMcaptcha: instance.enableMcaptcha, + mcaptchaSiteKey: instance.mcaptchaSitekey, + mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl, enableRecaptcha: instance.enableRecaptcha, recaptchaSiteKey: instance.recaptchaSiteKey, enableTurnstile: instance.enableTurnstile, diff --git a/packages/frontend/assets/drop-and-fusion/cold_face.png b/packages/frontend/assets/drop-and-fusion/cold_face.png new file mode 100644 index 0000000000..f5f53e9efc Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/cold_face.png differ diff --git a/packages/frontend/assets/drop-and-fusion/drop-arrow.svg b/packages/frontend/assets/drop-and-fusion/drop-arrow.svg new file mode 100644 index 0000000000..f98bb8a1ac --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/drop-arrow.svg @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="100%" height="100%" viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"> + <path d="M0,0L128,0L64,64L0,0Z" style="fill:rgb(255,61,0);"/> + <path d="M0,0L128,0L64,64L0,0ZM28.971,12L64,47.029C64,47.029 99.029,12 99.029,12L28.971,12Z" style="fill:rgb(255,122,0);"/> +</svg> diff --git a/packages/frontend/assets/drop-and-fusion/exploding_head.png b/packages/frontend/assets/drop-and-fusion/exploding_head.png new file mode 100644 index 0000000000..e8ec5182c8 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/exploding_head.png differ diff --git a/packages/frontend/assets/drop-and-fusion/face_with_open_mouth.png b/packages/frontend/assets/drop-and-fusion/face_with_open_mouth.png new file mode 100644 index 0000000000..c523020f62 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/face_with_open_mouth.png differ diff --git a/packages/frontend/assets/drop-and-fusion/face_with_symbols_on_mouth.png b/packages/frontend/assets/drop-and-fusion/face_with_symbols_on_mouth.png new file mode 100644 index 0000000000..db9e839c84 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/face_with_symbols_on_mouth.png differ diff --git a/packages/frontend/assets/drop-and-fusion/frame.svg b/packages/frontend/assets/drop-and-fusion/frame.svg new file mode 100644 index 0000000000..4276dae833 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/frame.svg @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="100%" height="100%" viewBox="0 0 450 600" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"> + <g> + <g transform="matrix(0.944444,0,0,0.875,12.5,62.5)"> + <rect x="0" y="0" width="450" height="600" style="fill:rgb(241,232,220);"/> + </g> + <use xlink:href="#_Image1" x="0" y="49.048" width="450px" height="551px"/> + </g> + <g transform="matrix(0.755719,0.654896,-0.654896,0.755719,383.517,-217.265)"> + <g transform="matrix(0.755719,-0.654896,0.654896,0.755719,-147.545,415.355)"> + <use xlink:href="#_Image2" x="0" y="49" width="450px" height="551px"/> + </g> + </g> + <use xlink:href="#_Image3" x="25" y="74.5" width="400px" height="500px"/> + <g transform="matrix(1,0,0,2,1.13687e-13,-12.5)"> + <rect x="25" y="37.5" width="400" height="12.5" style="fill:url(#_Linear4);"/> + </g> + <defs> + <image id="_Image1" width="450px" height="551px" xlink:href="data:image/png;base64,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CSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCpCSEAqQkhAKkJIQCp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"/> + <image id="_Image2" width="450px" height="551px" xlink:href="data:image/png;base64,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"/> + <image id="_Image3" width="400px" height="500px" xlink:href="data:image/png;base64,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"/> + <linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(7.65404e-16,12.5,-0.390625,2.39189e-17,225,37.5)"><stop offset="0" style="stop-color:rgb(255,14,0);stop-opacity:0.5"/><stop offset="1" style="stop-color:rgb(255,13,0);stop-opacity:0"/></linearGradient> + </defs> +</svg> diff --git a/packages/frontend/assets/drop-and-fusion/grinning_squinting_face.png b/packages/frontend/assets/drop-and-fusion/grinning_squinting_face.png new file mode 100644 index 0000000000..fd72d749a1 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/grinning_squinting_face.png differ diff --git a/packages/frontend/assets/drop-and-fusion/heart_suit.png b/packages/frontend/assets/drop-and-fusion/heart_suit.png new file mode 100644 index 0000000000..b0105f8582 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/heart_suit.png differ diff --git a/packages/frontend/assets/drop-and-fusion/pleading_face.png b/packages/frontend/assets/drop-and-fusion/pleading_face.png new file mode 100644 index 0000000000..42f58d411c Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/pleading_face.png differ diff --git a/packages/frontend/assets/drop-and-fusion/smiling_face_with_hearts.png b/packages/frontend/assets/drop-and-fusion/smiling_face_with_hearts.png new file mode 100644 index 0000000000..416ef0410a Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/smiling_face_with_hearts.png differ diff --git a/packages/frontend/assets/drop-and-fusion/smiling_face_with_sunglasses.png b/packages/frontend/assets/drop-and-fusion/smiling_face_with_sunglasses.png new file mode 100644 index 0000000000..c0f72254c2 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/smiling_face_with_sunglasses.png differ diff --git a/packages/frontend/assets/drop-and-fusion/zany_face.png b/packages/frontend/assets/drop-and-fusion/zany_face.png new file mode 100644 index 0000000000..f14f9db20b Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/zany_face.png differ diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 864779fd9d..7e7559d825 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -19,6 +19,7 @@ "dependencies": { "@discordapp/twemoji": "15.0.2", "@github/webauthn-json": "2.1.1", + "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@misskey-dev/browser-image-resizer": "2.2.1-misskey.10", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "5.0.5", diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index 40bca11e64..f60c721eae 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -6,12 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> <span v-if="!available">{{ i18n.ts.waiting }}<MkEllipsis/></span> - <div ref="captchaEl"></div> + <div v-if="props.provider == 'mcaptcha'"> + <div id="mcaptcha__widget-container" class="m-captcha-style"></div> + <div ref="captchaEl"></div> + </div> + <div v-else ref="captchaEl"></div> </div> </template> <script lang="ts" setup> -import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch } from 'vue'; +import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -26,7 +30,7 @@ export type Captcha = { getResponse(id: string): string; }; -export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile'; +export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile' | 'mcaptcha'; type CaptchaContainer = { readonly [_ in CaptchaProvider]?: Captcha; @@ -39,6 +43,7 @@ declare global { const props = defineProps<{ provider: CaptchaProvider; sitekey: string | null; // null will show error on request + instanceUrl?: string | null; modelValue?: string | null; }>(); @@ -55,6 +60,7 @@ const variable = computed(() => { case 'hcaptcha': return 'hcaptcha'; case 'recaptcha': return 'grecaptcha'; case 'turnstile': return 'turnstile'; + case 'mcaptcha': return 'mcaptcha'; } }); @@ -65,6 +71,7 @@ const src = computed(() => { case 'hcaptcha': return 'https://js.hcaptcha.com/1/api.js?render=explicit&recaptchacompat=off'; case 'recaptcha': return 'https://www.recaptcha.net/recaptcha/api.js?render=explicit'; case 'turnstile': return 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; + case 'mcaptcha': return null; } }); @@ -72,9 +79,9 @@ const scriptId = computed(() => `script-${props.provider}`); const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown as Captcha); -if (loaded) { +if (loaded || props.provider === 'mcaptcha') { available.value = true; -} else { +} else if (src.value !== null) { (document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), { async: true, id: scriptId.value, @@ -87,7 +94,7 @@ function reset() { if (captcha.value.reset) captcha.value.reset(); } -function requestRender() { +async function requestRender() { if (captcha.value.render && captchaEl.value instanceof Element) { captcha.value.render(captchaEl.value, { sitekey: props.sitekey, @@ -96,6 +103,15 @@ function requestRender() { 'expired-callback': callback, 'error-callback': callback, }); + } else if (props.provider === 'mcaptcha' && props.instanceUrl && props.sitekey) { + const { default: Widget } = await import('@mcaptcha/vanilla-glue'); + // @ts-expect-error avoid typecheck error + new Widget({ + siteKey: { + instanceUrl: new URL(props.instanceUrl), + key: props.sitekey, + }, + }); } else { window.setTimeout(requestRender, 1); } @@ -105,14 +121,27 @@ function callback(response?: string) { emit('update:modelValue', typeof response === 'string' ? response : null); } +function onReceivedMessage(message: MessageEvent) { + if (message.data.token) { + if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) { + callback(<string>message.data.token); + } + } +} + onMounted(() => { if (available.value) { + window.addEventListener('message', onReceivedMessage); requestRender(); } else { watch(available, requestRender); } }); +onUnmounted(() => { + window.removeEventListener('message', onReceivedMessage); +}); + onBeforeUnmount(() => { reset(); }); diff --git a/packages/frontend/src/components/MkPlusOneEffect.vue b/packages/frontend/src/components/MkPlusOneEffect.vue index a741a3f7a8..6feb85d8de 100644 --- a/packages/frontend/src/components/MkPlusOneEffect.vue +++ b/packages/frontend/src/components/MkPlusOneEffect.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }"> - <span class="text" :class="{ up }">+1</span> + <span class="text" :class="{ up }">+{{ value }}</span> </div> </template> @@ -16,7 +16,9 @@ import * as os from '@/os.js'; const props = withDefaults(defineProps<{ x: number; y: number; + value?: number; }>(), { + value: 1, }); const emit = defineEmits<{ diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 058de3a926..11b883e0f9 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -95,7 +95,8 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> - <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" + <MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/> + <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> @@ -156,6 +157,7 @@ const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>(''); const passwordRetypeState = ref<null | 'match' | 'not-match'>(null); const submitting = ref<boolean>(false); const hCaptchaResponse = ref<string | null>(null); +const mCaptchaResponse = ref<string | null>(null); const reCaptchaResponse = ref<string | null>(null); const turnstileResponse = ref<string | null>(null); const usernameAbortController = ref<null | AbortController>(null); @@ -164,7 +166,7 @@ const emailAbortController = ref<null | AbortController>(null); const shouldDisableSubmitting = computed((): boolean => { return submitting.value || instance.enableHcaptcha && !hCaptchaResponse.value || - instance.enableRecaptcha && !reCaptchaResponse.value || + instance.enableMcaptcha && !mCaptchaResponse.value ||instance.enableRecaptcha && !reCaptchaResponse.value || instance.enableTurnstile && !turnstileResponse.value || instance.emailRequiredForSignup && emailState.value !== 'ok' || usernameState.value !== 'ok' || @@ -291,7 +293,8 @@ async function onSubmit(): Promise<void> { emailAddress: email.value, invitationCode: invitationCode.value, 'hcaptcha-response': hCaptchaResponse.value, - 'g-recaptcha-response': reCaptchaResponse.value, + 'm-captcha-response': mCaptchaResponse.value, + 'g-recaptcha-response': reCaptchaResponse.value, 'turnstile-response': turnstileResponse.value, }); if (instance.emailRequiredForSignup) { diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html index 8de01e4802..13f800c72f 100644 --- a/packages/frontend/src/index.html +++ b/packages/frontend/src/index.html @@ -16,13 +16,13 @@ <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> <meta http-equiv="Content-Security-Policy" - content="default-src 'self'; + content="default-src 'self' https://newassets.hcaptcha.com/ https://challenges.cloudflare.com/ http://localhost:7493/; worker-src 'self'; - script-src 'self' 'unsafe-eval'; + script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; - connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;" + connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com;" /> <meta property="og:site_name" content="[DEV BUILD] Misskey" /> <meta name="viewport" content="width=device-width, initial-scale=1"> diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index 99b8070b71..37f8227485 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkRadios v-model="provider"> <option :value="null">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option> <option value="hcaptcha">hCaptcha</option> + <option value="mcaptcha">mCaptcha</option> <option value="recaptcha">reCAPTCHA</option> <option value="turnstile">Turnstile</option> </MkRadios> @@ -28,6 +29,24 @@ SPDX-License-Identifier: AGPL-3.0-only <MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/> </FormSlot> </template> + <template v-else-if="provider === 'mcaptcha'"> + <MkInput v-model="mcaptchaSiteKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.mcaptchaSiteKey }}</template> + </MkInput> + <MkInput v-model="mcaptchaSecretKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.mcaptchaSecretKey }}</template> + </MkInput> + <MkInput v-model="mcaptchaInstanceUrl"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template> + </MkInput> + <FormSlot v-if="mcaptchaSiteKey && mcaptchaInstanceUrl"> + <template #label>{{ i18n.ts.preview }}</template> + <MkCaptcha provider="mcaptcha" :sitekey="mcaptchaSiteKey" :instanceUrl="mcaptchaInstanceUrl"/> + </FormSlot> + </template> <template v-else-if="provider === 'recaptcha'"> <MkInput v-model="recaptchaSiteKey"> <template #prefix><i class="ti ti-key"></i></template> @@ -81,6 +100,9 @@ const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue' const provider = ref<CaptchaProvider | null>(null); const hcaptchaSiteKey = ref<string | null>(null); const hcaptchaSecretKey = ref<string | null>(null); +const mcaptchaSiteKey = ref<string | null>(null); +const mcaptchaSecretKey = ref<string | null>(null); +const mcaptchaInstanceUrl = ref<string | null>(null); const recaptchaSiteKey = ref<string | null>(null); const recaptchaSecretKey = ref<string | null>(null); const turnstileSiteKey = ref<string | null>(null); @@ -90,12 +112,18 @@ async function init() { const meta = await misskeyApi('admin/meta'); hcaptchaSiteKey.value = meta.hcaptchaSiteKey; hcaptchaSecretKey.value = meta.hcaptchaSecretKey; + mcaptchaSiteKey.value = meta.mcaptchaSiteKey; + mcaptchaSecretKey.value = meta.mcaptchaSecretKey; + mcaptchaInstanceUrl.value = meta.mcaptchaInstanceUrl; recaptchaSiteKey.value = meta.recaptchaSiteKey; recaptchaSecretKey.value = meta.recaptchaSecretKey; turnstileSiteKey.value = meta.turnstileSiteKey; turnstileSecretKey.value = meta.turnstileSecretKey; - provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null; + provider.value = meta.enableHcaptcha ? 'hcaptcha' : + meta.enableRecaptcha ? 'recaptcha' : + meta.enableTurnstile ? 'turnstile' : + meta.enableMcaptcha ? 'mcaptcha' : null; } function save() { @@ -103,6 +131,10 @@ function save() { enableHcaptcha: provider.value === 'hcaptcha', hcaptchaSiteKey: hcaptchaSiteKey.value, hcaptchaSecretKey: hcaptchaSecretKey.value, + enableMcaptcha: provider.value === 'mcaptcha', + mcaptchaSiteKey: mcaptchaSiteKey.value, + mcaptchaSecretKey: mcaptchaSecretKey.value, + mcaptchaInstanceUrl: mcaptchaInstanceUrl.value, enableRecaptcha: provider.value === 'recaptcha', recaptchaSiteKey: recaptchaSiteKey.value, recaptchaSecretKey: recaptchaSecretKey.value, diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index ec0c6166d0..a691d8ea1e 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #icon><i class="ti ti-shield"></i></template> <template #label>{{ i18n.ts.botProtection }}</template> <template v-if="enableHcaptcha" #suffix>hCaptcha</template> + <template v-else-if="enableMcaptcha" #suffix>mCaptcha</template> <template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template> <template v-else-if="enableTurnstile" #suffix>Turnstile</template> <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template> @@ -155,6 +156,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; const summalyProxy = ref<string>(''); const enableHcaptcha = ref<boolean>(false); +const enableMcaptcha = ref<boolean>(false); const enableRecaptcha = ref<boolean>(false); const enableTurnstile = ref<boolean>(false); const sensitiveMediaDetection = ref<string>('none'); @@ -174,6 +176,7 @@ async function init() { const meta = await misskeyApi('admin/meta'); summalyProxy.value = meta.summalyProxy; enableHcaptcha.value = meta.enableHcaptcha; + enableMcaptcha.value = meta.enableMcaptcha; enableRecaptcha.value = meta.enableRecaptcha; enableTurnstile.value = meta.enableTurnstile; sensitiveMediaDetection.value = meta.sensitiveMediaDetection; diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue new file mode 100644 index 0000000000..d0ca5157ef --- /dev/null +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -0,0 +1,761 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + <MkSpacer :contentMax="800"> + <div class="_gaps_s" :class="$style.root" style="margin: 0 auto;" :style="{ maxWidth: GAME_WIDTH + 'px' }"> + <div style="display: flex;"> + <div :class="$style.frame" style="flex: 1; margin-right: 10px;"> + <div :class="$style.frameInner"> + SCORE: <b><MkNumber :value="score"/></b> + </div> + </div> + <div :class="[$style.frame, $style.stock]" style="margin-left: auto;"> + <div :class="$style.frameInner" style="text-align: center;"> + NEXT >>> + <TransitionGroup + :enterActiveClass="$style.transition_stock_enterActive" + :leaveActiveClass="$style.transition_stock_leaveActive" + :enterFromClass="$style.transition_stock_enterFrom" + :leaveToClass="$style.transition_stock_leaveTo" + :moveClass="$style.transition_stock_move" + > + <div v-for="x in stock" :key="x.id" style="display: inline-block;"> + <img :src="x.fruit.img" style="width: 32px;"/> + </div> + </TransitionGroup> + </div> + </div> + </div> + <div :class="$style.main"> + <div ref="containerEl" :class="[$style.container, { [$style.gameOver]: gameOver }]" @click.stop.prevent="onClick" @touchmove="onTouchmove" @touchend="onTouchend" @mousemove="onMousemove"> + <img src="/client-assets/drop-and-fusion/frame.svg" :class="$style.mainFrameImg"/> + <canvas ref="canvasEl" :class="$style.canvas"/> + <Transition + :enterActiveClass="$style.transition_combo_enterActive" + :leaveActiveClass="$style.transition_combo_leaveActive" + :enterFromClass="$style.transition_combo_enterFrom" + :leaveToClass="$style.transition_combo_leaveTo" + :moveClass="$style.transition_combo_move" + > + <div v-show="combo > 1" :class="$style.combo" :style="{ fontSize: `${100 + ((comboPrev - 2) * 15)}%` }">{{ comboPrev }} Chain!</div> + </Transition> + <Transition + :enterActiveClass="$style.transition_picked_enterActive" + :leaveActiveClass="$style.transition_picked_leaveActive" + :enterFromClass="$style.transition_picked_enterFrom" + :leaveToClass="$style.transition_picked_leaveTo" + :moveClass="$style.transition_picked_move" + mode="out-in" + > + <img v-if="currentPick" :key="currentPick.id" :src="currentPick?.fruit.img" :class="$style.currentFruit" :style="{ top: -(currentPick?.fruit.size / 2) + 'px', left: (mouseX - (currentPick?.fruit.size / 2)) + 'px', width: `${currentPick?.fruit.size}px` }"/> + </Transition> + <template v-if="dropReady"> + <img src="/client-assets/drop-and-fusion/drop-arrow.svg" :class="$style.currentFruitArrow" :style="{ top: (currentPick?.fruit.size / 2) + 10 + 'px', left: (mouseX - 10) + 'px', width: `20px` }"/> + <div :class="$style.dropGuide" :style="{ left: (mouseX - 2) + 'px' }"/> + </template> + <div v-if="gameOver" :class="$style.gameOverLabel"> + <div>GAME OVER!</div> + <div>SCORE: <MkNumber :value="score"/></div> + </div> + </div> + </div> + <MkButton @click="restart">Restart</MkButton> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import * as Matter from 'matter-js'; +import { Ref, onMounted, ref, shallowRef } from 'vue'; +import { EventEmitter } from 'eventemitter3'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import * as sound from '@/scripts/sound.js'; +import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import * as os from '@/os.js'; +import MkNumber from '@/components/MkNumber.vue'; +import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue'; +import MkButton from '@/components/MkButton.vue'; + +const containerEl = shallowRef<HTMLElement>(); +const canvasEl = shallowRef<HTMLCanvasElement>(); +const mouseX = ref(0); + +const BASE_SIZE = 30; +const FRUITS = [{ + id: '9377076d-c980-4d83-bdaf-175bc58275b7', + level: 10, + size: BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + score: 512, + available: false, + sfxPitch: 0.25, + img: '/client-assets/drop-and-fusion/exploding_head.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: 'be9f38d2-b267-4b1a-b420-904e22e80568', + level: 9, + size: BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + score: 256, + available: false, + sfxPitch: 0.5, + img: '/client-assets/drop-and-fusion/face_with_symbols_on_mouth.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: 'beb30459-b064-4888-926b-f572e4e72e0c', + level: 8, + size: BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + score: 128, + available: false, + sfxPitch: 0.75, + img: '/client-assets/drop-and-fusion/cold_face.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: 'feab6426-d9d8-49ae-849c-048cdbb6cdf0', + level: 7, + size: BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + score: 64, + available: false, + sfxPitch: 1, + img: '/client-assets/drop-and-fusion/zany_face.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: 'd6d8fed6-6d18-4726-81a1-6cf2c974df8a', + level: 6, + size: BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + score: 32, + available: false, + sfxPitch: 1.5, + img: '/client-assets/drop-and-fusion/pleading_face.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: '249c728e-230f-4332-bbbf-281c271c75b2', + level: 5, + size: BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + score: 16, + available: true, + sfxPitch: 2, + img: '/client-assets/drop-and-fusion/face_with_open_mouth.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: '23d67613-d484-4a93-b71e-3e81b19d6186', + level: 4, + size: BASE_SIZE * 1.25 * 1.25 * 1.25, + score: 8, + available: true, + sfxPitch: 2.5, + img: '/client-assets/drop-and-fusion/smiling_face_with_sunglasses.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: '3cbd0add-ad7d-4685-bad0-29f6dddc0b99', + level: 3, + size: BASE_SIZE * 1.25 * 1.25, + score: 4, + available: true, + sfxPitch: 3, + img: '/client-assets/drop-and-fusion/grinning_squinting_face.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: '8f86d4f4-ee02-41bf-ad38-1ce0ae457fb5', + level: 2, + size: BASE_SIZE * 1.25, + score: 2, + available: true, + sfxPitch: 3.5, + img: '/client-assets/drop-and-fusion/smiling_face_with_hearts.png', + imgSize: 256, + spriteScale: 1.12, +}, { + id: '64ec4add-ce39-42b4-96cb-33908f3f118d', + level: 1, + size: BASE_SIZE, + score: 1, + available: true, + sfxPitch: 4, + img: '/client-assets/drop-and-fusion/heart_suit.png', + imgSize: 256, + spriteScale: 1.12, +}] as const; + +const GAME_WIDTH = 450; +const GAME_HEIGHT = 600; +const PHYSICS_QUALITY_FACTOR = 32; // 低いほどパフォーマンスが高いがガタガタして安定しなくなる + +let viewScaleX = 1; +let viewScaleY = 1; +const currentPick = shallowRef<{ id: string; fruit: typeof FRUITS[number] } | null>(null); +const stock = shallowRef<{ id: string; fruit: typeof FRUITS[number] }[]>([]); +const score = ref(0); +const combo = ref(0); +const comboPrev = ref(0); +const dropReady = ref(true); +const gameOver = ref(false); +const gameStarted = ref(false); + +class Game extends EventEmitter<{ + changeScore: (score: number) => void; + changeCombo: (combo: number) => void; + changeStock: (stock: { id: string; fruit: typeof FRUITS[number] }[]) => void; + dropped: () => void; + fusioned: (x: number, y: number, score: number) => void; + gameOver: () => void; +}> { + private COMBO_INTERVAL = 1000; + public readonly DROP_INTERVAL = 500; + private PLAYAREA_MARGIN = 25; + private engine: Matter.Engine; + private render: Matter.Render; + private runner: Matter.Runner; + private detector: Matter.Detector; + private overflowCollider: Matter.Body; + private isGameOver = false; + + /** + * フィールドに出ていて、かつ合体の対象となるアイテム + */ + private activeBodyIds: Matter.Body['id'][] = []; + + private latestDroppedBodyId: Matter.Body['id'] | null = null; + + private latestDroppedAt = 0; + private latestFusionedAt = 0; + private stock: { id: string; fruit: typeof FRUITS[number] }[] = []; + + private _combo = 0; + private get combo() { + return this._combo; + } + private set combo(value: number) { + this._combo = value; + this.emit('changeCombo', value); + } + + private _score = 0; + private get score() { + return this._score; + } + private set score(value: number) { + this._score = value; + this.emit('changeScore', value); + } + + constructor() { + super(); + + this.engine = Matter.Engine.create({ + constraintIterations: 2 * PHYSICS_QUALITY_FACTOR, + positionIterations: 6 * PHYSICS_QUALITY_FACTOR, + velocityIterations: 4 * PHYSICS_QUALITY_FACTOR, + gravity: { + x: 0, + y: 1, + }, + timing: { + timeScale: 2, + }, + enableSleeping: false, + }); + + this.render = Matter.Render.create({ + engine: this.engine, + canvas: canvasEl.value, + options: { + width: GAME_WIDTH, + height: GAME_HEIGHT, + background: 'transparent', // transparent to hide + wireframeBackground: 'transparent', // transparent to hide + wireframes: false, + showSleeping: false, + pixelRatio: window.devicePixelRatio, + }, + }); + + Matter.Render.run(this.render); + + this.runner = Matter.Runner.create(); + Matter.Runner.run(this.runner, this.engine); + + this.detector = Matter.Detector.create(); + + this.engine.world.bodies = []; + + //#region walls + const WALL_OPTIONS: Matter.IChamferableBodyDefinition = { + isStatic: true, + render: { + strokeStyle: 'transparent', + fillStyle: 'transparent', + }, + }; + + const thickness = 100; + Matter.Composite.add(this.engine.world, [ + Matter.Bodies.rectangle(GAME_WIDTH / 2, GAME_HEIGHT + (thickness / 2) - this.PLAYAREA_MARGIN, GAME_WIDTH, thickness, WALL_OPTIONS), + Matter.Bodies.rectangle(GAME_WIDTH + (thickness / 2) - this.PLAYAREA_MARGIN, GAME_HEIGHT / 2, thickness, GAME_HEIGHT, WALL_OPTIONS), + Matter.Bodies.rectangle(-((thickness / 2) - this.PLAYAREA_MARGIN), GAME_HEIGHT / 2, thickness, GAME_HEIGHT, WALL_OPTIONS), + ]); + //#endregion + + this.overflowCollider = Matter.Bodies.rectangle(GAME_WIDTH / 2, 0, GAME_WIDTH, 125, { + isStatic: true, + isSensor: true, + render: { + strokeStyle: 'transparent', + fillStyle: 'transparent', + }, + }); + Matter.Composite.add(this.engine.world, this.overflowCollider); + + // fit the render viewport to the scene + Matter.Render.lookAt(this.render, { + min: { x: 0, y: 0 }, + max: { x: GAME_WIDTH, y: GAME_HEIGHT }, + }); + } + + private createBody(fruit: typeof FRUITS[number], x: number, y: number) { + return Matter.Bodies.circle(x, y, fruit.size / 2, { + label: fruit.id, + density: 0.0005, + frictionAir: 0.01, + restitution: 0.4, + friction: 0.5, + frictionStatic: 5, + //mass: 0, + render: { + sprite: { + texture: fruit.img, + xScale: (fruit.size / fruit.imgSize) * fruit.spriteScale, + yScale: (fruit.size / fruit.imgSize) * fruit.spriteScale, + }, + }, + }); + } + + private fusion(bodyA: Matter.Body, bodyB: Matter.Body) { + const now = Date.now(); + if (this.latestFusionedAt > now - this.COMBO_INTERVAL) { + this.combo++; + } else { + this.combo = 1; + } + this.latestFusionedAt = now; + + // TODO: 単に位置だけでなくそれぞれの動きベクトルも融合する + const newX = (bodyA.position.x + bodyB.position.x) / 2; + const newY = (bodyA.position.y + bodyB.position.y) / 2; + + Matter.Composite.remove(this.engine.world, [bodyA, bodyB]); + this.activeBodyIds = this.activeBodyIds.filter(x => x !== bodyA.id && x !== bodyB.id); + + const currentFruit = FRUITS.find(y => y.id === bodyA.label)!; + const nextFruit = FRUITS.find(x => x.level === currentFruit.level + 1); + + if (nextFruit) { + const body = this.createBody(nextFruit, newX, newY); + Matter.Composite.add(this.engine.world, body); + + // 連鎖してfusionした場合の分かりやすさのため少し間を置いてからfusion対象になるようにする + window.setTimeout(() => { + this.activeBodyIds.push(body.id); + }, 100); + + const additionalScore = Math.round(currentFruit.score * (1 + (this.combo / 3))); + this.score += additionalScore; + + const pan = ((newX / GAME_WIDTH) - 0.5) * 2; + sound.playRaw('syuilo/bubble2', 1, pan, nextFruit.sfxPitch); + + this.emit('fusioned', newX, newY, additionalScore); + } else { + //const VELOCITY = 30; + //for (let i = 0; i < 10; i++) { + // const body = createBody(FRUITS.find(x => x.level === (1 + Math.floor(Math.random() * 3)))!, x + ((Math.random() * VELOCITY) - (VELOCITY / 2)), y + ((Math.random() * VELOCITY) - (VELOCITY / 2))); + // Matter.Composite.add(world, body); + // bodies.push(body); + //} + //sound.playRaw({ + // type: 'syuilo/bubble2', + // volume: 1, + //}); + } + } + + private gameOver() { + this.isGameOver = true; + Matter.Runner.stop(this.runner); + this.emit('gameOver'); + } + + public start() { + for (let i = 0; i < 4; i++) { + this.stock.push({ + id: Math.random().toString(), + fruit: FRUITS.filter(x => x.available)[Math.floor(Math.random() * FRUITS.filter(x => x.available).length)], + }); + } + this.emit('changeStock', this.stock); + + // TODO: fusion予約状態のアイテムは光らせるなどの演出をすると楽しそう + let fusionReservedPairs: { bodyA: Matter.Body; bodyB: Matter.Body }[] = []; + + const minCollisionDepthForSound = 2.5; + const maxCollisionDepthForSound = 9; + const soundPitchMax = 4; + const soundPitchMin = 0.5; + + Matter.Events.on(this.engine, 'collisionStart', (event) => { + for (const pairs of event.pairs) { + const { bodyA, bodyB } = pairs; + if (bodyA.id === this.overflowCollider.id || bodyB.id === this.overflowCollider.id) { + if (bodyA.id === this.latestDroppedBodyId || bodyB.id === this.latestDroppedBodyId) { + continue; + } + this.gameOver(); + break; + } + const shouldFusion = (bodyA.label === bodyB.label) && !fusionReservedPairs.some(x => x.bodyA.id === bodyA.id || x.bodyA.id === bodyB.id || x.bodyB.id === bodyA.id || x.bodyB.id === bodyB.id); + if (shouldFusion) { + if (this.activeBodyIds.includes(bodyA.id) && this.activeBodyIds.includes(bodyB.id)) { + this.fusion(bodyA, bodyB); + } else { + fusionReservedPairs.push({ bodyA, bodyB }); + window.setTimeout(() => { + fusionReservedPairs = fusionReservedPairs.filter(x => x.bodyA.id !== bodyA.id && x.bodyB.id !== bodyB.id); + this.fusion(bodyA, bodyB); + }, 100); + } + } else { + const energy = pairs.collision.depth; + if (energy > minCollisionDepthForSound) { + const vol = (Math.min(maxCollisionDepthForSound, energy - minCollisionDepthForSound) / maxCollisionDepthForSound) / 4; + const pan = ((((bodyA.position.x + bodyB.position.x) / 2) / GAME_WIDTH) - 0.5) * 2; + const pitch = soundPitchMin + ((soundPitchMax - soundPitchMin) * (1 - (Math.min(10, energy) / 10))); + sound.playRaw('syuilo/poi1', vol, pan, pitch); + } + } + } + }); + + window.setInterval(() => { + if (this.latestFusionedAt < Date.now() - this.COMBO_INTERVAL) { + this.combo = 0; + } + }, 500); + } + + public drop(_x: number) { + if (this.isGameOver) return; + if (Date.now() - this.latestDroppedAt < this.DROP_INTERVAL) { + return; + } + const st = this.stock.shift()!; + this.stock.push({ + id: Math.random().toString(), + fruit: FRUITS.filter(x => x.available)[Math.floor(Math.random() * FRUITS.filter(x => x.available).length)], + }); + this.emit('changeStock', this.stock); + + const x = Math.min(GAME_WIDTH - this.PLAYAREA_MARGIN - (st.fruit.size / 2), Math.max(this.PLAYAREA_MARGIN + (st.fruit.size / 2), _x)); + const body = this.createBody(st.fruit, x, st.fruit.size / 2); + Matter.Composite.add(this.engine.world, body); + this.activeBodyIds.push(body.id); + this.latestDroppedBodyId = body.id; + this.latestDroppedAt = Date.now(); + this.emit('dropped'); + const pan = ((x / GAME_WIDTH) - 0.5) * 2; + sound.playRaw('syuilo/poi2', 1, pan); + } + + public dispose() { + Matter.Render.stop(this.render); + Matter.Runner.stop(this.runner); + Matter.World.clear(this.engine.world, false); + Matter.Engine.clear(this.engine); + } +} + +let game: Game; + +function onClick(ev: MouseEvent) { + const rect = containerEl.value.getBoundingClientRect(); + + const x = (ev.clientX - rect.left) / viewScaleX; + + game.drop(x); +} + +function onTouchend(ev: TouchEvent) { + const rect = containerEl.value.getBoundingClientRect(); + + const x = (ev.changedTouches[0].clientX - rect.left) / viewScaleX; + + game.drop(x); +} + +function onMousemove(ev: MouseEvent) { + mouseX.value = ev.clientX - containerEl.value.getBoundingClientRect().left; +} + +function onTouchmove(ev: TouchEvent) { + mouseX.value = ev.touches[0].clientX - containerEl.value.getBoundingClientRect().left; +} + +function restart() { + game.dispose(); + gameOver.value = false; + currentPick.value = null; + dropReady.value = true; + stock.value = []; + score.value = 0; + combo.value = 0; + comboPrev.value = 0; + game = new Game(); + attachGame(); + game.start(); +} + +function attachGame() { + game.addListener('changeScore', value => { + score.value = value; + }); + + game.addListener('changeCombo', value => { + if (value === 0) { + comboPrev.value = combo.value; + } else { + comboPrev.value = value; + } + combo.value = value; + }); + + game.addListener('changeStock', value => { + currentPick.value = JSON.parse(JSON.stringify(value[0])); + stock.value = JSON.parse(JSON.stringify(value.slice(1))); + }); + + game.addListener('dropped', () => { + dropReady.value = false; + window.setTimeout(() => { + if (!gameOver.value) { + dropReady.value = true; + } + }, game.DROP_INTERVAL); + }); + + game.addListener('fusioned', (x, y, score) => { + const rect = canvasEl.value.getBoundingClientRect(); + const domX = rect.left + (x * viewScaleX); + const domY = rect.top + (y * viewScaleY); + os.popup(MkRippleEffect, { x: domX, y: domY }, {}, 'end'); + os.popup(MkPlusOneEffect, { x: domX, y: domY, value: score }, {}, 'end'); + }); + + game.addListener('gameOver', () => { + currentPick.value = null; + dropReady.value = false; + gameOver.value = true; + }); +} + +onMounted(() => { + game = new Game(); + + attachGame(); + + game.start(); + + const actualCanvasWidth = canvasEl.value.getBoundingClientRect().width; + const actualCanvasHeight = canvasEl.value.getBoundingClientRect().height; + viewScaleX = actualCanvasWidth / GAME_WIDTH; + viewScaleY = actualCanvasHeight / GAME_HEIGHT; +}); + +definePageMetadata({ + title: 'Drop & Fusion', + icon: 'ti ti-apple', +}); +</script> + +<style lang="scss" module> +.transition_stock_move, +.transition_stock_enterActive, +.transition_stock_leaveActive { + transition: opacity 0.4s cubic-bezier(0,.5,.5,1), transform 0.4s cubic-bezier(0,.5,.5,1) !important; +} +.transition_stock_enterFrom, +.transition_stock_leaveTo { + opacity: 0; + transform: scale(0.7); +} +.transition_stock_leaveActive { + position: absolute; +} + +.transition_picked_move, +.transition_picked_enterActive { + transition: opacity 0.5s cubic-bezier(0,.5,.5,1), transform 0.5s cubic-bezier(0,.5,.5,1) !important; +} +.transition_picked_leaveActive { + transition: all 0s !important; +} +.transition_picked_enterFrom, +.transition_picked_leaveTo { + opacity: 0; + transform: translateY(-50px); +} +.transition_picked_leaveActive { + position: absolute; +} + +.transition_combo_move, +.transition_combo_enterActive { + transition: all 0s !important; +} +.transition_combo_leaveActive { + transition: opacity 0.4s cubic-bezier(0,.5,.5,1), transform 0.4s cubic-bezier(0,.5,.5,1) !important; +} +.transition_combo_enterFrom, +.transition_combo_leaveTo { + opacity: 0; + transform: scale(0.7); +} +.transition_combo_leaveActive { + position: absolute; +} + +.root { + user-select: none; + + * { + user-select: none; + } +} + +.frame { + padding: 7px; + background: #8C4F26; + box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; + border-radius: 10px; +} +.frameInner { + padding: 4px 8px; + background: #F1E8DC; + box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; + border-radius: 6px; + color: #693410; +} + +.main { + position: relative; +} + +.mainFrameImg { + position: absolute; + top: 0; + left: 0; + width: 100%; + filter: drop-shadow(0 6px 16px #0007); + pointer-events: none; + user-select: none; +} + +.canvas { + position: relative; + display: block; + z-index: 1; + margin-top: -50px; + max-width: 100%; + pointer-events: none; + user-select: none; +} + +.container { + position: relative; +} + +.stock { + pointer-events: none; + user-select: none; +} + +.combo { + position: absolute; + z-index: 3; + top: 50%; + width: 100%; + text-align: center; + font-weight: bold; + font-style: oblique; + pointer-events: none; + user-select: none; +} + +.currentFruit { + position: absolute; + margin-top: 20px; + z-index: 2; + filter: drop-shadow(0 6px 16px #0007); + pointer-events: none; + user-select: none; +} + +.currentFruitArrow { + position: absolute; + margin-top: 20px; + z-index: 3; + animation: currentFruitArrow 2s ease infinite; + pointer-events: none; + user-select: none; +} + +.dropGuide { + position: absolute; + top: 50px; + z-index: 3; + width: 3px; + height: calc(100% - 50px); + background: #f002; + pointer-events: none; + user-select: none; +} + +.gameOverLabel { + position: absolute; + z-index: 10; + top: 50%; + width: 100%; + padding: 16px; + box-sizing: border-box; + background: #0007; + color: #fff; + text-align: center; + font-weight: bold; +} + +.gameOver { + .canvas { + filter: grayscale(1); + } +} + +@keyframes currentFruitArrow { + 0% { transform: translateY(0); } + 25% { transform: translateY(-8px); } + 50% { transform: translateY(0); } + 75% { transform: translateY(-8px); } + 100% { transform: translateY(0); } +} +</style> diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index d626bfcdb6..7603bb0fb3 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -531,6 +531,10 @@ export const routes = [{ path: '/clicker', component: page(() => import('./pages/clicker.vue')), loginRequired: true, +}, { + path: '/drop-and-fusion', + component: page(() => import('./pages/drop-and-fusion.vue')), + loginRequired: true, }, { path: '/timeline', component: page(() => import('./pages/timeline.vue')), diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts index 222fd9b0b7..f7e0369419 100644 --- a/packages/frontend/src/scripts/form.ts +++ b/packages/frontend/src/scripts/form.ts @@ -3,7 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -type EnumItem = string | {label: string; value: string;}; +type EnumItem = string | { + label: string; + value: string; +}; + export type FormItem = { label?: string; type: 'string'; @@ -36,16 +40,23 @@ export type FormItem = { label: string; value: unknown; }[]; +} | { + label?: string; + type: 'range'; + default: number | null; + step: number; + min: number; + max: number; } | { label?: string; type: 'object'; default: Record<string, unknown> | null; - hidden: true; + hidden: boolean; } | { label?: string; type: 'array'; default: unknown[] | null; - hidden: true; + hidden: boolean; }; export type Form = Record<string, FormItem>; @@ -55,6 +66,7 @@ type GetItemType<Item extends FormItem> = Item['type'] extends 'number' ? number : Item['type'] extends 'boolean' ? boolean : Item['type'] extends 'radio' ? unknown : + Item['type'] extends 'range' ? number : Item['type'] extends 'enum' ? string : Item['type'] extends 'array' ? unknown[] : Item['type'] extends 'object' ? Record<string, unknown> diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 0b966ff199..acde78f5fd 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -92,7 +92,13 @@ export type OperationType = typeof operationTypes[number]; * @param soundStore サウンド設定 * @param options `useCache`: デフォルトは`true` 一度再生した音声はキャッシュする */ -export async function loadAudio(soundStore: SoundStore, options?: { useCache?: boolean; }) { +export async function loadAudio(soundStore: { + type: Exclude<SoundType, '_driveFile_'>; +} | { + type: '_driveFile_'; + fileId: string; + fileUrl: string; +}, options?: { useCache?: boolean; }) { if (_DEV_) console.log('loading audio. opts:', options); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) { @@ -179,18 +185,31 @@ export async function playFile(soundStore: SoundStore) { createSourceNode(buffer, soundStore.volume)?.start(); } -export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBufferSourceNode | null { +export async function playRaw(type: Exclude<SoundType, '_driveFile_'>, volume = 1, pan = 0, playbackRate = 1) { + const buffer = await loadAudio({ type }); + if (!buffer) return; + createSourceNode(buffer, volume, pan, playbackRate)?.start(); +} + +export function createSourceNode(buffer: AudioBuffer, volume: number, pan = 0, playbackRate = 1) : AudioBufferSourceNode | null { const masterVolume = defaultStore.state.sound_masterVolume; if (isMute() || masterVolume === 0 || volume === 0) { return null; } + const panNode = ctx.createStereoPanner(); + panNode.pan.value = pan; + const gainNode = ctx.createGain(); gainNode.gain.value = masterVolume * volume; const soundSource = ctx.createBufferSource(); soundSource.buffer = buffer; - soundSource.connect(gainNode).connect(ctx.destination); + soundSource.playbackRate.value = playbackRate; + soundSource + .connect(panNode) + .connect(gainNode) + .connect(ctx.destination); return soundSource; } diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index 5251ef5787..d4639df685 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -27,6 +27,11 @@ function toolsMenuItems(): MenuItem[] { to: '/clicker', text: '●👈', icon: 'ti ti-cookie', + }, { + type: 'link', + to: '/drop-and-fusion', + text: 'Drop & Fusion', + icon: 'ti ti-apple', }, ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) ? { type: 'link', to: '/custom-emojis-manager', diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index e8722cab3b..43d80734e9 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1,6 +1,6 @@ /* * version: 2023.12.2 - * generatedAt: 2024-01-02T08:53:57.449Z + * generatedAt: 2024-01-04T18:10:15.096Z */ import type { SwitchCaseResponseType } from '../api.js'; diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 192a1a31e0..07ee46ace9 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -1,6 +1,6 @@ /* * version: 2023.12.2 - * generatedAt: 2024-01-02T08:53:57.445Z + * generatedAt: 2024-01-04T18:10:15.094Z */ import type { diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index fd4d7372cc..546d90ce21 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -1,6 +1,6 @@ /* * version: 2023.12.2 - * generatedAt: 2024-01-02T08:53:57.443Z + * generatedAt: 2024-01-04T18:10:15.093Z */ import { operations } from './types.js'; diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts index db0ada0f3b..59e4bc2f60 100644 --- a/packages/misskey-js/src/autogen/models.ts +++ b/packages/misskey-js/src/autogen/models.ts @@ -1,6 +1,6 @@ /* * version: 2023.12.2 - * generatedAt: 2024-01-02T08:53:57.441Z + * generatedAt: 2024-01-04T18:10:15.091Z */ import { components } from './types.js'; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index cf7100c007..b9c569f96b 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3,7 +3,7 @@ /* * version: 2023.12.2 - * generatedAt: 2024-01-02T08:53:56.447Z + * generatedAt: 2024-01-04T18:10:15.023Z */ /** @@ -4400,6 +4400,9 @@ export type operations = { emailRequiredForSignup: boolean; enableHcaptcha: boolean; hcaptchaSiteKey: string | null; + enableMcaptcha: boolean; + mcaptchaSiteKey: string | null; + mcaptchaInstanceUrl: string | null; enableRecaptcha: boolean; recaptchaSiteKey: string | null; enableTurnstile: boolean; @@ -4425,6 +4428,7 @@ export type operations = { bannedEmailDomains?: string[]; preservedUsernames: string[]; hcaptchaSecretKey: string | null; + mcaptchaSecretKey: string | null; recaptchaSecretKey: string | null; turnstileSecretKey: string | null; sensitiveMediaDetection: string; @@ -8198,6 +8202,10 @@ export type operations = { enableHcaptcha?: boolean; hcaptchaSiteKey?: string | null; hcaptchaSecretKey?: string | null; + enableMcaptcha?: boolean; + mcaptchaSiteKey?: string | null; + mcaptchaInstanceUrl?: string | null; + mcaptchaSecretKey?: string | null; enableRecaptcha?: boolean; recaptchaSiteKey?: string | null; recaptchaSecretKey?: string | null; @@ -18706,6 +18714,9 @@ export type operations = { emailRequiredForSignup: boolean; enableHcaptcha: boolean; hcaptchaSiteKey: string | null; + enableMcaptcha: boolean; + mcaptchaSiteKey: string | null; + mcaptchaInstanceUrl: string | null; enableRecaptcha: boolean; recaptchaSiteKey: string | null; enableTurnstile: boolean; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 562c90595e..28cfe3222f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -661,6 +661,9 @@ importers: '@github/webauthn-json': specifier: 2.1.1 version: 2.1.1 + '@mcaptcha/vanilla-glue': + specifier: 0.1.0-alpha-3 + version: 0.1.0-alpha-3 '@misskey-dev/browser-image-resizer': specifier: 2.2.1-misskey.10 version: 2.2.1-misskey.10 @@ -1820,7 +1823,7 @@ packages: '@babel/traverse': 7.22.11 '@babel/types': 7.22.17 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1843,7 +1846,7 @@ packages: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1945,7 +1948,7 @@ packages: '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -3345,7 +3348,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.5 '@babel/types': 7.22.17 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -3363,7 +3366,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.5 '@babel/types': 7.23.5 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -4242,7 +4245,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) espree: 9.6.1 globals: 13.19.0 ignore: 5.2.4 @@ -4259,7 +4262,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) espree: 9.6.1 globals: 13.19.0 ignore: 5.2.4 @@ -4524,7 +4527,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -4902,6 +4905,16 @@ packages: dev: false optional: true + /@mcaptcha/core-glue@0.1.0-alpha-5: + resolution: {integrity: sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA==} + dev: false + + /@mcaptcha/vanilla-glue@0.1.0-alpha-3: + resolution: {integrity: sha512-GT6TJBgmViGXcXiT5VOr+h/6iOnThSlZuCoOWncubyTZU9R3cgU5vWPkF7G6Ob6ee2CBe3yqBxxk24CFVGTVXw==} + dependencies: + '@mcaptcha/core-glue': 0.1.0-alpha-5 + dev: false + /@mdx-js/react@2.3.0(react@18.2.0): resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} peerDependencies: @@ -5084,7 +5097,7 @@ packages: '@open-draft/until': 1.0.3 '@types/debug': 4.1.7 '@xmldom/xmldom': 0.8.6 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) headers-polyfill: 3.2.5 outvariant: 1.4.0 strict-event-emitter: 0.2.8 @@ -7365,7 +7378,7 @@ packages: hasBin: true peerDependencies: '@swc/core': ^1.2.66 - chokidar: 3.5.3 + chokidar: ^3.5.1 peerDependenciesMeta: chokidar: optional: true @@ -8493,7 +8506,7 @@ packages: '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.53.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -8522,7 +8535,7 @@ packages: '@typescript-eslint/type-utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.14.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.56.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -8548,7 +8561,7 @@ packages: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.53.0 typescript: 5.3.3 transitivePeerDependencies: @@ -8569,7 +8582,7 @@ packages: '@typescript-eslint/types': 6.14.0 '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.14.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.56.0 typescript: 5.3.3 transitivePeerDependencies: @@ -8604,7 +8617,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.53.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 @@ -8624,7 +8637,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.56.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 @@ -8653,7 +8666,7 @@ packages: dependencies: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -8674,7 +8687,7 @@ packages: dependencies: '@typescript-eslint/types': 6.14.0 '@typescript-eslint/visitor-keys': 6.14.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -9131,7 +9144,7 @@ packages: engines: {node: '>= 6.0.0'} requiresBuild: true dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -9139,7 +9152,7 @@ packages: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: false @@ -9514,7 +9527,7 @@ packages: resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==} dependencies: archy: 1.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) fastq: 1.15.0 transitivePeerDependencies: - supports-color @@ -10948,7 +10961,6 @@ packages: dependencies: ms: 2.1.2 supports-color: 5.5.0 - dev: true /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} @@ -10961,6 +10973,7 @@ packages: dependencies: ms: 2.1.2 supports-color: 8.1.1 + dev: true /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -11177,7 +11190,7 @@ packages: hasBin: true dependencies: address: 1.2.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: true @@ -11501,7 +11514,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -11840,7 +11853,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -11887,7 +11900,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -12491,7 +12504,7 @@ packages: debug: optional: true dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -13043,7 +13056,6 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -13181,7 +13193,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: false @@ -13243,7 +13255,7 @@ packages: engines: {node: '>= 6.0.0'} dependencies: agent-base: 5.1.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: true @@ -13253,7 +13265,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -13262,7 +13274,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: false @@ -13272,7 +13284,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: false @@ -13422,7 +13434,7 @@ packages: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -13863,7 +13875,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: @@ -14541,7 +14553,7 @@ packages: resolution: {integrity: sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==} engines: {node: '>=10'} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) rfdc: 1.3.0 uri-js: 4.4.1 transitivePeerDependencies: @@ -17109,7 +17121,7 @@ packages: engines: {node: '>=8.16.0'} dependencies: '@types/mime-types': 2.1.4 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -18108,7 +18120,7 @@ packages: dependencies: '@hapi/hoek': 10.0.1 '@hapi/wreck': 18.0.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) joi: 17.7.0 transitivePeerDependencies: - supports-color @@ -18308,7 +18320,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -18461,7 +18473,7 @@ packages: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -18726,7 +18738,6 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -19343,7 +19354,7 @@ packages: chalk: 4.1.2 cli-highlight: 2.1.11 date-fns: 2.30.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) dotenv: 16.0.3 glob: 8.1.0 ioredis: 5.3.2 @@ -19701,7 +19712,7 @@ packages: hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 @@ -19813,7 +19824,7 @@ packages: acorn-walk: 8.2.0 cac: 6.7.14 chai: 4.3.10 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) happy-dom: 10.0.3 local-pkg: 0.4.3 magic-string: 0.30.3 @@ -19895,7 +19906,7 @@ packages: peerDependencies: eslint: '>=6.0.0' dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.56.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3