From 7ac5a5735852a169496b45c37438aa49a3fa7551 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:30:01 +0000 Subject: [PATCH 1/2] feat(api): api update --- .stats.yml | 4 +- .../ContentSubmitParams/Policy/URLRisk.php | 61 ++++- .../Policy/EntityMatcherOutput/Match_.php | 79 ++++++- .../EntityMatcherOutput/Match_/Signals.php | 208 ++++++++++++++++++ .../Match_/Signals/BrandImpersonation.php | 83 +++++++ .../Signals/BrandImpersonation/Method.php | 12 + 6 files changed, 440 insertions(+), 7 deletions(-) create mode 100644 src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals.php create mode 100644 src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation.php create mode 100644 src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation/Method.php diff --git a/.stats.yml b/.stats.yml index 188067d..423ab65 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 27 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/moderation-api%2Fmoderation-api-a4934bf1e7f1348c021b48224f7a7110a6e41838253dda4fbcc720dd2d2ed6b7.yml -openapi_spec_hash: 537542216811907b1d4ebf23a54dc669 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/moderation-api%2Fmoderation-api-c8e66e827fc2b1465b740a29e87da71c3b1ddca1a4bdb1023aa96c569b80e9be.yml +openapi_spec_hash: 35fdc3e34feb56cafaf4de2834201978 config_hash: 0a024bca1710e3a3194925edfedc513c diff --git a/src/Content/ContentSubmitParams/Policy/URLRisk.php b/src/Content/ContentSubmitParams/Policy/URLRisk.php index cd1cbfe..d2e7853 100644 --- a/src/Content/ContentSubmitParams/Policy/URLRisk.php +++ b/src/Content/ContentSubmitParams/Policy/URLRisk.php @@ -11,7 +11,11 @@ /** * @phpstan-type URLRiskShape = array{ - * id: 'url_risk', flag: bool, threshold?: float|null + * id: 'url_risk', + * flag: bool, + * allowlistWordlistIDs?: list|null, + * blocklistWordlistIDs?: list|null, + * threshold?: float|null, * } */ final class URLRisk implements BaseModel @@ -26,6 +30,22 @@ final class URLRisk implements BaseModel #[Required] public bool $flag; + /** + * IDs of wordlists whose entries are treated as allowed URL domains. Matches short-circuit the risk model and are never flagged. + * + * @var list|null $allowlistWordlistIDs + */ + #[Optional('allowlistWordlistIds', list: 'string')] + public ?array $allowlistWordlistIDs; + + /** + * IDs of wordlists whose entries are treated as blocked URL domains. Matches short-circuit the risk model and are always flagged. Blocklists take precedence over allowlists. + * + * @var list|null $blocklistWordlistIDs + */ + #[Optional('blocklistWordlistIds', list: 'string')] + public ?array $blocklistWordlistIDs; + #[Optional] public ?float $threshold; @@ -52,13 +72,22 @@ public function __construct() * Construct an instance from the required parameters. * * You must use named parameters to construct any parameters with a default value. + * + * @param list|null $allowlistWordlistIDs + * @param list|null $blocklistWordlistIDs */ - public static function with(bool $flag, ?float $threshold = null): self - { + public static function with( + bool $flag, + ?array $allowlistWordlistIDs = null, + ?array $blocklistWordlistIDs = null, + ?float $threshold = null, + ): self { $self = new self; $self['flag'] = $flag; + null !== $allowlistWordlistIDs && $self['allowlistWordlistIDs'] = $allowlistWordlistIDs; + null !== $blocklistWordlistIDs && $self['blocklistWordlistIDs'] = $blocklistWordlistIDs; null !== $threshold && $self['threshold'] = $threshold; return $self; @@ -83,6 +112,32 @@ public function withFlag(bool $flag): self return $self; } + /** + * IDs of wordlists whose entries are treated as allowed URL domains. Matches short-circuit the risk model and are never flagged. + * + * @param list $allowlistWordlistIDs + */ + public function withAllowlistWordlistIDs(array $allowlistWordlistIDs): self + { + $self = clone $this; + $self['allowlistWordlistIDs'] = $allowlistWordlistIDs; + + return $self; + } + + /** + * IDs of wordlists whose entries are treated as blocked URL domains. Matches short-circuit the risk model and are always flagged. Blocklists take precedence over allowlists. + * + * @param list $blocklistWordlistIDs + */ + public function withBlocklistWordlistIDs(array $blocklistWordlistIDs): self + { + $self = clone $this; + $self['blocklistWordlistIDs'] = $blocklistWordlistIDs; + + return $self; + } + public function withThreshold(float $threshold): self { $self = clone $this; diff --git a/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_.php b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_.php index b99e803..0029f8f 100644 --- a/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_.php +++ b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_.php @@ -4,13 +4,22 @@ namespace ModerationAPI\Content\ContentSubmitResponse\Policy\EntityMatcherOutput; +use ModerationAPI\Content\ContentSubmitResponse\Policy\EntityMatcherOutput\Match_\Signals; +use ModerationAPI\Core\Attributes\Optional; use ModerationAPI\Core\Attributes\Required; use ModerationAPI\Core\Concerns\SdkModel; use ModerationAPI\Core\Contracts\BaseModel; /** + * @phpstan-import-type SignalsShape from \ModerationAPI\Content\ContentSubmitResponse\Policy\EntityMatcherOutput\Match_\Signals + * * @phpstan-type MatchShape = array{ - * match: string, probability: float, span: list + * match: string, + * probability: float, + * span: list, + * entityType?: string|null, + * reasons?: list|null, + * signals?: null|Signals|SignalsShape, * } */ final class Match_ implements BaseModel @@ -28,6 +37,26 @@ final class Match_ implements BaseModel #[Required(list: 'int')] public array $span; + /** + * Sub-type of the entity match — e.g. the NER key (email, phone, name, …) for PII matches. Absent for URL Risk and wordlist matches where the type is already encoded in the parent label. + */ + #[Optional('entity_type')] + public ?string $entityType; + + /** + * Stable codes explaining why a URL was flagged (URL Risk only). + * + * @var list|null $reasons + */ + #[Optional(list: 'string')] + public ?array $reasons; + + /** + * Observable properties of a URL (URL Risk only). Absent for allow/block list matches. + */ + #[Optional] + public ?Signals $signals; + /** * `new Match_()` is missing required properties by the API. * @@ -53,11 +82,16 @@ public function __construct() * You must use named parameters to construct any parameters with a default value. * * @param list $span + * @param list|null $reasons + * @param Signals|SignalsShape|null $signals */ public static function with( string $match, float $probability, - array $span + array $span, + ?string $entityType = null, + ?array $reasons = null, + Signals|array|null $signals = null, ): self { $self = new self; @@ -65,6 +99,10 @@ public static function with( $self['probability'] = $probability; $self['span'] = $span; + null !== $entityType && $self['entityType'] = $entityType; + null !== $reasons && $self['reasons'] = $reasons; + null !== $signals && $self['signals'] = $signals; + return $self; } @@ -94,4 +132,41 @@ public function withSpan(array $span): self return $self; } + + /** + * Sub-type of the entity match — e.g. the NER key (email, phone, name, …) for PII matches. Absent for URL Risk and wordlist matches where the type is already encoded in the parent label. + */ + public function withEntityType(string $entityType): self + { + $self = clone $this; + $self['entityType'] = $entityType; + + return $self; + } + + /** + * Stable codes explaining why a URL was flagged (URL Risk only). + * + * @param list $reasons + */ + public function withReasons(array $reasons): self + { + $self = clone $this; + $self['reasons'] = $reasons; + + return $self; + } + + /** + * Observable properties of a URL (URL Risk only). Absent for allow/block list matches. + * + * @param Signals|SignalsShape $signals + */ + public function withSignals(Signals|array $signals): self + { + $self = clone $this; + $self['signals'] = $signals; + + return $self; + } } diff --git a/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals.php b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals.php new file mode 100644 index 0000000..36c0ca5 --- /dev/null +++ b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals.php @@ -0,0 +1,208 @@ + */ + use SdkModel; + + #[Required('bot_protection')] + public ?bool $botProtection; + + #[Required('brand_impersonation')] + public ?BrandImpersonation $brandImpersonation; + + #[Required('domain_age_days')] + public ?int $domainAgeDays; + + #[Required('final_url')] + public ?string $finalURL; + + #[Required('has_email_setup')] + public ?bool $hasEmailSetup; + + #[Required('has_suspicious_characters')] + public bool $hasSuspiciousCharacters; + + #[Required('is_link_shortener')] + public bool $isLinkShortener; + + #[Required('is_reported')] + public bool $isReported; + + #[Required('redirect_count')] + public ?int $redirectCount; + + /** + * `new Signals()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * Signals::with( + * botProtection: ..., + * brandImpersonation: ..., + * domainAgeDays: ..., + * finalURL: ..., + * hasEmailSetup: ..., + * hasSuspiciousCharacters: ..., + * isLinkShortener: ..., + * isReported: ..., + * redirectCount: ..., + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new Signals) + * ->withBotProtection(...) + * ->withBrandImpersonation(...) + * ->withDomainAgeDays(...) + * ->withFinalURL(...) + * ->withHasEmailSetup(...) + * ->withHasSuspiciousCharacters(...) + * ->withIsLinkShortener(...) + * ->withIsReported(...) + * ->withRedirectCount(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param BrandImpersonation|BrandImpersonationShape|null $brandImpersonation + */ + public static function with( + ?bool $botProtection, + BrandImpersonation|array|null $brandImpersonation, + ?int $domainAgeDays, + ?string $finalURL, + ?bool $hasEmailSetup, + bool $hasSuspiciousCharacters, + bool $isLinkShortener, + bool $isReported, + ?int $redirectCount, + ): self { + $self = new self; + + $self['botProtection'] = $botProtection; + $self['brandImpersonation'] = $brandImpersonation; + $self['domainAgeDays'] = $domainAgeDays; + $self['finalURL'] = $finalURL; + $self['hasEmailSetup'] = $hasEmailSetup; + $self['hasSuspiciousCharacters'] = $hasSuspiciousCharacters; + $self['isLinkShortener'] = $isLinkShortener; + $self['isReported'] = $isReported; + $self['redirectCount'] = $redirectCount; + + return $self; + } + + public function withBotProtection(?bool $botProtection): self + { + $self = clone $this; + $self['botProtection'] = $botProtection; + + return $self; + } + + /** + * @param BrandImpersonation|BrandImpersonationShape|null $brandImpersonation + */ + public function withBrandImpersonation( + BrandImpersonation|array|null $brandImpersonation + ): self { + $self = clone $this; + $self['brandImpersonation'] = $brandImpersonation; + + return $self; + } + + public function withDomainAgeDays(?int $domainAgeDays): self + { + $self = clone $this; + $self['domainAgeDays'] = $domainAgeDays; + + return $self; + } + + public function withFinalURL(?string $finalURL): self + { + $self = clone $this; + $self['finalURL'] = $finalURL; + + return $self; + } + + public function withHasEmailSetup(?bool $hasEmailSetup): self + { + $self = clone $this; + $self['hasEmailSetup'] = $hasEmailSetup; + + return $self; + } + + public function withHasSuspiciousCharacters( + bool $hasSuspiciousCharacters + ): self { + $self = clone $this; + $self['hasSuspiciousCharacters'] = $hasSuspiciousCharacters; + + return $self; + } + + public function withIsLinkShortener(bool $isLinkShortener): self + { + $self = clone $this; + $self['isLinkShortener'] = $isLinkShortener; + + return $self; + } + + public function withIsReported(bool $isReported): self + { + $self = clone $this; + $self['isReported'] = $isReported; + + return $self; + } + + public function withRedirectCount(?int $redirectCount): self + { + $self = clone $this; + $self['redirectCount'] = $redirectCount; + + return $self; + } +} diff --git a/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation.php b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation.php new file mode 100644 index 0000000..0b90ee4 --- /dev/null +++ b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation.php @@ -0,0 +1,83 @@ + + * } + */ +final class BrandImpersonation implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $brand; + + /** @var value-of $method */ + #[Required(enum: Method::class)] + public string $method; + + /** + * `new BrandImpersonation()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandImpersonation::with(brand: ..., method: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandImpersonation)->withBrand(...)->withMethod(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Method|value-of $method + */ + public static function with(string $brand, Method|string $method): self + { + $self = new self; + + $self['brand'] = $brand; + $self['method'] = $method; + + return $self; + } + + public function withBrand(string $brand): self + { + $self = clone $this; + $self['brand'] = $brand; + + return $self; + } + + /** + * @param Method|value-of $method + */ + public function withMethod(Method|string $method): self + { + $self = clone $this; + $self['method'] = $method; + + return $self; + } +} diff --git a/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation/Method.php b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation/Method.php new file mode 100644 index 0000000..d505c8b --- /dev/null +++ b/src/Content/ContentSubmitResponse/Policy/EntityMatcherOutput/Match_/Signals/BrandImpersonation/Method.php @@ -0,0 +1,12 @@ + Date: Fri, 24 Apr 2026 13:30:23 +0000 Subject: [PATCH 2/2] release: 0.12.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ README.md | 2 +- src/Version.php | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5332797..a713055 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.11.3" + ".": "0.12.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 253ed42..f1a147d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.12.0 (2026-04-24) + +Full Changelog: [v0.11.3...v0.12.0](https://github.com/moderation-api/sdk-php/compare/v0.11.3...v0.12.0) + +### Features + +* **api:** api update ([7ac5a57](https://github.com/moderation-api/sdk-php/commit/7ac5a5735852a169496b45c37438aa49a3fa7551)) + ## 0.11.3 (2026-04-18) Full Changelog: [v0.11.2...v0.11.3](https://github.com/moderation-api/sdk-php/compare/v0.11.2...v0.11.3) diff --git a/README.md b/README.md index 1bcb96e..c18abe6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The REST API documentation can be found on [docs.moderationapi.com](https://docs ``` -composer require "moderation-api/sdk-php 0.11.3" +composer require "moderation-api/sdk-php 0.12.0" ``` diff --git a/src/Version.php b/src/Version.php index 1bad0ad..7ccaed7 100644 --- a/src/Version.php +++ b/src/Version.php @@ -5,5 +5,5 @@ namespace ModerationAPI; // x-release-please-start-version -const VERSION = '0.11.3'; +const VERSION = '0.12.0'; // x-release-please-end