diff --git a/locales/en-US.yml b/locales/en-US.yml index a44447a25..f2ef64beb 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2575,4 +2575,18 @@ _reversi: _offlineScreen: title: "Offline - cannot connect to the server" header: "Unable to connect to the server" - +_skebStatus: + _genres: + art: "Artwork" + comic: "Comic" + voice: "Voice" + novel: "Text" + video: "Movie" + music: "Music" + correction: "Advice" + seeking: "Seeking" + stopped: "Stopped" + client: "Client" + yenX: "JPY {x}" + nWorks: "Delivered {n} works" + nRequests: "Requested {n} times" diff --git a/locales/index.d.ts b/locales/index.d.ts index 70beffa0c..ecce896b6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -10113,6 +10113,62 @@ export interface Locale extends ILocale { */ "summaryProxyDescription2": string; }; + "_skebStatus": { + "_genres": { + /** + * イラスト + */ + "art": string; + /** + * コミック + */ + "comic": string; + /** + * ボイス + */ + "voice": string; + /** + * テキスト + */ + "novel": string; + /** + * ムービー + */ + "video": string; + /** + * ミュージック + */ + "music": string; + /** + * アドバイス + */ + "correction": string; + }; + /** + * 募集中 + */ + "seeking": string; + /** + * 停止中 + */ + "stopped": string; + /** + * クライアント + */ + "client": string; + /** + * {x}円 + */ + "yenX": ParameterizedString<"x">; + /** + * 納品実績 {n}件 + */ + "nWorks": ParameterizedString<"n">; + /** + * 取引実績 {n}件 + */ + "nRequests": ParameterizedString<"n">; + }; } declare const locales: { [lang: string]: Locale; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ec4f688eb..6ac606959 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2693,3 +2693,19 @@ _urlPreviewSetting: summaryProxy: "プレビューを生成するプロキシのエンドポイント" summaryProxyDescription: "Misskey本体ではなく、サマリープロキシを使用してプレビューを生成します。" summaryProxyDescription2: "プロキシには下記パラメータがクエリ文字列として連携されます。プロキシ側がこれらをサポートしない場合、設定値は無視されます。" + +_skebStatus: + _genres: + art: "イラスト" + comic: "コミック" + voice: "ボイス" + novel: "テキスト" + video: "ムービー" + music: "ミュージック" + correction: "アドバイス" + seeking: "募集中" + stopped: "停止中" + client: "クライアント" + yenX: "{x}円" + nWorks: "納品実績 {n}件" + nRequests: "取引実績 {n}件" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index a736dcc23..7f5fb8eb7 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -2549,4 +2549,18 @@ _reversi: _offlineScreen: title: "오프라인 - 서버에 접속할 수 없습니다" header: "서버에 접속할 수 없습니다" - +_skebStatus: + _genres: + art: "작품" + comic: "만화" + voice: "음성" + novel: "텍스트" + video: "동영상" + music: "음악" + correction: "조언" + seeking: "모집 중" + stopped: "정지 중" + client: "클라이언트" + yenX: "JPY {x}" + nWorks: "납품 실적 {n}건" + nRequests: "거래 실적 {n}건" diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 04212e35c..8502d1ef1 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -66,6 +66,14 @@ type Source = { scope?: 'local' | 'global' | string[]; }; + skebStatus?: { + method: string; + endpoint: string; + headers: { [x: string]: string }; + parameters: { [x: string]: string }; + userIdParameterName: string; + } + proxy?: string; proxySmtp?: string; proxyBypassHosts?: string[]; @@ -140,6 +148,13 @@ export type Config = { index: string; scope?: 'local' | 'global' | string[]; } | undefined; + skebStatus: { + method: string; + endpoint: string; + headers: { [x: string]: string }; + parameters: { [x: string]: string }; + userIdParameterName: string; + } | undefined; proxy: string | undefined; proxySmtp: string | undefined; proxyBypassHosts: string[] | undefined; @@ -266,6 +281,7 @@ export function loadConfig(): Config { redisForObjectStorageQueue: config.redisForObjectStorageQueue ? convertRedisOptions(config.redisForObjectStorageQueue, host) : redisForJobQueue, redisForWebhookDeliverQueue: config.redisForWebhookDeliverQueue ? convertRedisOptions(config.redisForWebhookDeliverQueue, host) : redisForJobQueue, redisForTimelines: config.redisForTimelines ? convertRedisOptions(config.redisForTimelines, host) : redis, + skebStatus: config.skebStatus, id: config.id, proxy: config.proxy, proxySmtp: config.proxySmtp, diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts index 27f0364dd..7fbde9008 100644 --- a/packages/backend/src/core/entities/MetaEntityService.ts +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -112,6 +112,7 @@ export class MetaEntityService { mediaProxy: this.config.mediaProxy, enableUrlPreview: instance.urlPreviewEnabled, + enableSkebStatus: !!this.config.skebStatus, }; } diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts index 633cc1cc0..bff146114 100644 --- a/packages/backend/src/models/json-schema/meta.ts +++ b/packages/backend/src/models/json-schema/meta.ts @@ -216,6 +216,10 @@ export const packedMetaLiteSchema = { type: 'boolean', optional: false, nullable: false, }, + enableSkebStatus: { + type: 'boolean', + optional: false, nullable: false, + }, backgroundImageUrl: { type: 'string', optional: false, nullable: true, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 7842f0e28..85a39548a 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -350,6 +350,7 @@ import * as ep___users_following from './endpoints/users/following.js'; import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_getFollowingBirthdayUsers from './endpoints/users/get-following-birthday-users.js'; import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; +import * as ep___users_getSkebStatus from './endpoints/users/get-skeb-status.js'; import * as ep___users_featuredNotes from './endpoints/users/featured-notes.js'; import * as ep___users_lists_create from './endpoints/users/lists/create.js'; import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; @@ -736,6 +737,7 @@ const $users_following: Provider = { provide: 'ep:users/following', useClass: ep const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }; const $users_getFollowingBirthdayUsers: Provider = { provide: 'ep:users/get-following-birthday-users', useClass: ep___users_getFollowingBirthdayUsers.default }; const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }; +const $users_getSkebStatus: Provider = { provide: 'ep:users/get-skeb-status', useClass: ep___users_getSkebStatus.default }; const $users_featuredNotes: Provider = { provide: 'ep:users/featured-notes', useClass: ep___users_featuredNotes.default }; const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }; const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }; @@ -1126,6 +1128,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $users_gallery_posts, $users_getFollowingBirthdayUsers, $users_getFrequentlyRepliedUsers, + $users_getSkebStatus, $users_featuredNotes, $users_lists_create, $users_lists_delete, @@ -1508,6 +1511,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $users_gallery_posts, $users_getFollowingBirthdayUsers, $users_getFrequentlyRepliedUsers, + $users_getSkebStatus, $users_featuredNotes, $users_lists_create, $users_lists_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index be8c4f080..368ffdca7 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -350,6 +350,7 @@ import * as ep___users_following from './endpoints/users/following.js'; import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_getFollowingBirthdayUsers from './endpoints/users/get-following-birthday-users.js'; import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; +import * as ep___users_getSkebStatus from './endpoints/users/get-skeb-status.js'; import * as ep___users_featuredNotes from './endpoints/users/featured-notes.js'; import * as ep___users_lists_create from './endpoints/users/lists/create.js'; import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; @@ -734,6 +735,7 @@ const eps = [ ['users/gallery/posts', ep___users_gallery_posts], ['users/get-following-birthday-users', ep___users_getFollowingBirthdayUsers], ['users/get-frequently-replied-users', ep___users_getFrequentlyRepliedUsers], + ['users/get-skeb-status', ep___users_getSkebStatus], ['users/featured-notes', ep___users_featuredNotes], ['users/lists/create', ep___users_lists_create], ['users/lists/delete', ep___users_lists_delete], diff --git a/packages/backend/src/server/api/endpoints/users/get-skeb-status.ts b/packages/backend/src/server/api/endpoints/users/get-skeb-status.ts new file mode 100644 index 000000000..79b23ae73 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/users/get-skeb-status.ts @@ -0,0 +1,148 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import type { Config } from '@/config.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['users'], + + requireCredential: false, + allowGet: true, + cacheSec: 60 * 5, + + res: { + type: 'object', + properties: { + screenName: { + type: 'string', + optional: false, nullable: false, + }, + isCreator: { + type: 'boolean', + optional: false, nullable: false, + }, + isAcceptable: { + type: 'boolean', + optional: false, nullable: false, + }, + creatorRequestCount: { + type: 'integer', + optional: false, nullable: false, + }, + clientRequestCount: { + type: 'integer', + optional: false, nullable: false, + }, + skills: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + properties: { + amount: { + type: 'integer', + optional: false, nullable: false, + }, + genre: { + type: 'string', + optional: false, nullable: false, + enum: ['art', 'comic', 'voice', 'novel', 'video', 'music', 'correction'], + }, + }, + }, + }, + }, + }, + + errors: { + skebStatusNotAvailable: { + message: 'Skeb status is not available.', + code: 'SKEB_STATUS_NOT_AVAILABLE', + id: '1dd37c9c-7e97-4c24-be98-227a78b21d80', + httpStatusCode: 403, + }, + + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '88d582ae-69d9-45e0-a8b3-13f9945e48bf', + httpStatusCode: 404, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.config) + private config: Config, + + private loggerService: LoggerService, + private httpRequestService: HttpRequestService, + ) { + super(meta, paramDef, async (ps) => { + if (!this.config.skebStatus) throw new ApiError(meta.errors.skebStatusNotAvailable); + const logger = this.loggerService.getLogger('api:users:get-skeb-status'); + + const url = new URL(this.config.skebStatus.endpoint); + for (const [key, value] of Object.entries(this.config.skebStatus.parameters)) { + url.searchParams.set(key, value); + } + url.searchParams.set(this.config.skebStatus.userIdParameterName, ps.userId); + + logger.info('Requesting Skeb status', { url: url.href, userId: ps.userId }); + const res = await this.httpRequestService.send( + url.href, + { + method: this.config.skebStatus.method, + headers: { + ...this.config.skebStatus.headers, + Accept: 'application/json', + }, + timeout: 5000, + }, + { + throwErrorWhenResponseNotOk: false, + }, + ); + + const json = (await res.json()) as { + screen_name: string, + is_creator: boolean, + is_acceptable: boolean, + creator_request_count: number, + client_request_count: number, + skills: { amount: number, genre: 'art' | 'comic' | 'voice' | 'novel' | 'video' | 'music' | 'correction' }[], + ban_reason?: string | null + error?: unknown, + }; + + if (res.status > 399 || (json.error ?? json.ban_reason)) { + logger.error('Skeb status response error', { url: url.href, userId: ps.userId, status: res.status, statusText: res.statusText, error: json.error ?? json.ban_reason }); + throw new ApiError(meta.errors.noSuchUser); + } + + logger.info('Skeb status response', { url: url.href, userId: ps.userId, status: res.status, statusText: res.statusText, skebStatus: json }); + + return { + screenName: json.screen_name, + isCreator: json.is_creator, + isAcceptable: json.is_acceptable, + creatorRequestCount: json.creator_request_count, + clientRequestCount: json.client_request_count, + skills: json.skills, + }; + }); + } +} diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 82b9e3804..8ca02f699 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ dateString(user.createdAt) }} ()
-
+
@@ -104,6 +104,40 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
+ + + + + + + + + + + + + Skeb + +
+
+ + {{ i18n.ts._skebStatus.seeking }} + + + {{ i18n.ts._skebStatus.stopped }} + + + {{ i18n.ts._skebStatus.client }} + + +
+
@@ -167,10 +201,11 @@ import number from '@/filters/number.js'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; +import { instance } from '@/instance.js'; import { $i, iAmModerator } from '@/account.js'; import { dateString } from '@/filters/date.js'; import { confetti } from '@/scripts/confetti.js'; -import { misskeyApi } from '@/scripts/misskey-api.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; import { useRouter } from '@/router/supplier.js'; @@ -204,6 +239,7 @@ const props = withDefaults(defineProps<{ const router = useRouter(); const user = ref(props.user); +const userSkebStatus = ref(null); const parallaxAnimationId = ref(null); const narrow = ref(null); const rootEl = ref(null); @@ -273,8 +309,44 @@ async function updateMemo() { isEditingMemo.value = false; } +async function fetchSkebStatus() { + if (!instance.enableSkebStatus || !props.user.id) { + userSkebStatus.value = null; + return; + } + + console.log('fetching skeb status'); + userSkebStatus.value = await misskeyApiGet('users/get-skeb-status', { userId: props.user.id }); +} + +function buildSkebStatus(): string { + if (!userSkebStatus.value) return ''; + + if (userSkebStatus.value.isCreator) { + let status = ''; + + if (userSkebStatus.value.isAcceptable) { + status += `${i18n.ts._skebStatus._genres[userSkebStatus.value.skills[0].genre]} ${i18n.tsx._skebStatus.yenX({ x: userSkebStatus.value.skills[0].amount.toLocaleString() })}`; + } + + if (userSkebStatus.value.creatorRequestCount > 0) { + if (userSkebStatus.value.isAcceptable) { + status += ' | '; + } + status += i18n.tsx._skebStatus.nWorks({ n: userSkebStatus.value.creatorRequestCount.toLocaleString() }); + } + + return status; + } else if (userSkebStatus.value.clientRequestCount > 0) { + return i18n.tsx._skebStatus.nRequests({ n: userSkebStatus.value.clientRequestCount.toLocaleString() }); + } + + return ''; +} + watch([props.user], () => { memoDraft.value = props.user.memo; + fetchSkebStatus(); }); onMounted(() => { @@ -292,6 +364,7 @@ onMounted(() => { }); } } + fetchSkebStatus(); nextTick(() => { adjustMemoTextarea(); }); @@ -685,4 +758,29 @@ onUnmounted(() => { margin-left: 4px; color: var(--success); } + +.skebAcceptable, +.skebStopped, +.skebClient { + display: inline-flex; + border: solid 1px; + border-radius: 6px; + padding: 2px 6px; + font-size: 85%; +} + +.skebAcceptable { + color: rgb(255, 255, 255); + background-color: rgb(241, 70, 104); +} + +.skebStopped { + color: rgb(255, 255, 255); + background-color: rgb(54, 54, 54); +} + +.skebClient { + color: rgb(255, 255, 255); + background-color: rgb(54, 54, 54); +} diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 3289b4f7c..e5f23163c 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1690,6 +1690,8 @@ declare namespace entities { UsersGetFollowingBirthdayUsersResponse, UsersGetFrequentlyRepliedUsersRequest, UsersGetFrequentlyRepliedUsersResponse, + UsersGetSkebStatusRequest, + UsersGetSkebStatusResponse, UsersFeaturedNotesRequest, UsersFeaturedNotesResponse, UsersListsCreateRequest, @@ -3105,6 +3107,12 @@ type UsersGetFrequentlyRepliedUsersRequest = operations['users___get-frequently- // @public (undocumented) type UsersGetFrequentlyRepliedUsersResponse = operations['users___get-frequently-replied-users']['responses']['200']['content']['application/json']; +// @public (undocumented) +type UsersGetSkebStatusRequest = operations['users___get-skeb-status']['requestBody']['content']['application/json']; + +// @public (undocumented) +type UsersGetSkebStatusResponse = operations['users___get-skeb-status']['responses']['200']['content']['application/json']; + // @public (undocumented) type UsersListsCreateFromPublicRequest = operations['users___lists___create-from-public']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index 35d746190..f25943a4f 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -3826,6 +3826,17 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *No* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 2dd65df9a..6ddc534ea 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -510,6 +510,8 @@ import type { UsersGetFollowingBirthdayUsersResponse, UsersGetFrequentlyRepliedUsersRequest, UsersGetFrequentlyRepliedUsersResponse, + UsersGetSkebStatusRequest, + UsersGetSkebStatusResponse, UsersFeaturedNotesRequest, UsersFeaturedNotesResponse, UsersListsCreateRequest, @@ -920,6 +922,7 @@ export type Endpoints = { 'users/gallery/posts': { req: UsersGalleryPostsRequest; res: UsersGalleryPostsResponse }; 'users/get-following-birthday-users': { req: UsersGetFollowingBirthdayUsersRequest; res: UsersGetFollowingBirthdayUsersResponse }; 'users/get-frequently-replied-users': { req: UsersGetFrequentlyRepliedUsersRequest; res: UsersGetFrequentlyRepliedUsersResponse }; + 'users/get-skeb-status': { req: UsersGetSkebStatusRequest; res: UsersGetSkebStatusResponse }; 'users/featured-notes': { req: UsersFeaturedNotesRequest; res: UsersFeaturedNotesResponse }; 'users/lists/create': { req: UsersListsCreateRequest; res: UsersListsCreateResponse }; 'users/lists/delete': { req: UsersListsDeleteRequest; res: EmptyResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index 2c9afc650..bc5b420a4 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -513,6 +513,8 @@ export type UsersGetFollowingBirthdayUsersRequest = operations['users___get-foll export type UsersGetFollowingBirthdayUsersResponse = operations['users___get-following-birthday-users']['responses']['200']['content']['application/json']; export type UsersGetFrequentlyRepliedUsersRequest = operations['users___get-frequently-replied-users']['requestBody']['content']['application/json']; export type UsersGetFrequentlyRepliedUsersResponse = operations['users___get-frequently-replied-users']['responses']['200']['content']['application/json']; +export type UsersGetSkebStatusRequest = operations['users___get-skeb-status']['requestBody']['content']['application/json']; +export type UsersGetSkebStatusResponse = operations['users___get-skeb-status']['responses']['200']['content']['application/json']; export type UsersFeaturedNotesRequest = operations['users___featured-notes']['requestBody']['content']['application/json']; export type UsersFeaturedNotesResponse = operations['users___featured-notes']['responses']['200']['content']['application/json']; export type UsersListsCreateRequest = operations['users___lists___create']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 83250ce4a..398da6bbe 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3294,6 +3294,22 @@ export type paths = { */ post: operations['users___get-frequently-replied-users']; }; + '/users/get-skeb-status': { + /** + * users/get-skeb-status + * @description No description provided. + * + * **Credential required**: *No* + */ + get: operations['users___get-skeb-status']; + /** + * users/get-skeb-status + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['users___get-skeb-status']; + }; '/users/featured-notes': { /** * users/featured-notes @@ -5008,6 +5024,7 @@ export type components = { translatorAvailable: boolean; mediaProxy: string; enableUrlPreview: boolean; + enableSkebStatus: boolean; backgroundImageUrl: string | null; impressumUrl: string | null; logoImageUrl: string | null; @@ -25669,6 +25686,71 @@ export type operations = { }; }; }; + /** + * users/get-skeb-status + * @description No description provided. + * + * **Credential required**: *No* + */ + 'users___get-skeb-status': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + userId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + screenName: string; + isCreator: boolean; + isAcceptable: boolean; + creatorRequestCount: number; + clientRequestCount: number; + skills: ({ + amount: number; + /** @enum {string} */ + genre: 'art' | 'comic' | 'voice' | 'novel' | 'video' | 'music' | 'correction'; + })[]; + }; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * users/featured-notes * @description No description provided.