This commit is contained in:
tamaina 2023-05-10 15:50:15 +00:00
parent f3bc232419
commit d3ecb02644
25 changed files with 225 additions and 254 deletions

View File

@ -4,83 +4,7 @@ import type { User } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { NotificationService } from '@/core/NotificationService.js'; import { NotificationService } from '@/core/NotificationService.js';
import { ACHIEVEMENT_TYPES } from 'misskey-js';
export const ACHIEVEMENT_TYPES = [
'notes1',
'notes10',
'notes100',
'notes500',
'notes1000',
'notes5000',
'notes10000',
'notes20000',
'notes30000',
'notes40000',
'notes50000',
'notes60000',
'notes70000',
'notes80000',
'notes90000',
'notes100000',
'login3',
'login7',
'login15',
'login30',
'login60',
'login100',
'login200',
'login300',
'login400',
'login500',
'login600',
'login700',
'login800',
'login900',
'login1000',
'passedSinceAccountCreated1',
'passedSinceAccountCreated2',
'passedSinceAccountCreated3',
'loggedInOnBirthday',
'loggedInOnNewYearsDay',
'noteClipped1',
'noteFavorited1',
'myNoteFavorited1',
'profileFilled',
'markedAsCat',
'following1',
'following10',
'following50',
'following100',
'following300',
'followers1',
'followers10',
'followers50',
'followers100',
'followers300',
'followers500',
'followers1000',
'collectAchievements30',
'viewAchievements3min',
'iLoveMisskey',
'foundTreasure',
'client30min',
'client60min',
'noteDeletedWithin1min',
'postedAtLateNight',
'postedAt0min0sec',
'selfQuote',
'htl20npm',
'viewInstanceChart',
'outputHelloWorldOnScratchpad',
'open3windows',
'driveFolderCircularReference',
'reactWithoutRead',
'clickedClickHere',
'justPlainLucky',
'setNameToSyuilo',
'cookieClicked',
'brainDiver',
] as const;
@Injectable() @Injectable()
export class AchievementService { export class AchievementService {

View File

@ -9,7 +9,7 @@ import type { Note } from '@/models/entities/Note.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { isNotNull } from '@/misc/is-not-null.js'; import { isNotNull } from '@/misc/is-not-null.js';
import { notificationTypes } from '@/types.js'; import { notificationTypes } from 'misskey-js';
import type { OnModuleInit } from '@nestjs/common'; import type { OnModuleInit } from '@nestjs/common';
import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js';
import type { UserEntityService } from './UserEntityService.js'; import type { UserEntityService } from './UserEntityService.js';

View File

@ -1,6 +1,6 @@
import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { id } from '../id.js'; import { id } from '../id.js';
import { mutedNoteReasons } from '../../types.js'; import { mutedNoteReasons } from 'misskey-js';
import { Note } from './Note.js'; import { Note } from './Note.js';
import { User } from './User.js'; import { User } from './User.js';

View File

@ -1,6 +1,6 @@
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { id } from '../id.js'; import { id } from '../id.js';
import { noteVisibilities } from '../../types.js'; import { noteVisibilities } from 'misskey-js';
import { User } from './User.js'; import { User } from './User.js';
import { Channel } from './Channel.js'; import { Channel } from './Channel.js';
import type { DriveFile } from './DriveFile.js'; import type { DriveFile } from './DriveFile.js';

View File

@ -1,4 +1,4 @@
import { notificationTypes } from '@/types.js'; import { notificationTypes } from 'misskey-js';
import { User } from './User.js'; import { User } from './User.js';
import { Note } from './Note.js'; import { Note } from './Note.js';
import { FollowRequest } from './FollowRequest.js'; import { FollowRequest } from './FollowRequest.js';

View File

@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
import { id } from '../id.js'; import { id } from '../id.js';
import { noteVisibilities } from '../../types.js'; import { noteVisibilities } from 'misskey-js';
import { Note } from './Note.js'; import { Note } from './Note.js';
import type { User } from './User.js'; import type { User } from './User.js';

View File

@ -1,5 +1,5 @@
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from '@/types.js'; import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from 'misskey-js';
import { id } from '../id.js'; import { id } from '../id.js';
import { User } from './User.js'; import { User } from './User.js';
import { Page } from './Page.js'; import { Page } from './Page.js';

View File

@ -1,4 +1,4 @@
import { notificationTypes } from '@/types.js'; import { notificationTypes } from 'misskey-js';
export const packedNotificationSchema = { export const packedNotificationSchema = {
type: 'object', type: 'object',

View File

@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import { AchievementService, ACHIEVEMENT_TYPES } from '@/core/AchievementService.js'; import { AchievementService } from '@/core/AchievementService.js';
import { ACHIEVEMENT_TYPES } from 'misskey-js';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,

View File

@ -2,7 +2,7 @@ import { Brackets, In } from 'typeorm';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js'; import type { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js';
import { obsoleteNotificationTypes, notificationTypes } from '@/types.js'; import { obsoleteNotificationTypes, notificationTypes } from 'misskey-js';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js'; import { QueryService } from '@/core/QueryService.js';
import { NoteReadService } from '@/core/NoteReadService.js'; import { NoteReadService } from '@/core/NoteReadService.js';

View File

@ -8,7 +8,7 @@ import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, Pag
import type { User } from '@/models/entities/User.js'; import type { User } from '@/models/entities/User.js';
import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/User.js';
import type { UserProfile } from '@/models/entities/UserProfile.js'; import type { UserProfile } from '@/models/entities/UserProfile.js';
import { notificationTypes } from '@/types.js'; import { notificationTypes } from 'misskey-js';
import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { langmap } from '@/misc/langmap.js'; import { langmap } from '@/misc/langmap.js';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';

View File

@ -1,8 +0,0 @@
export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const;
export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const;
export const ffVisibility = ['public', 'followers', 'private'] as const;

View File

@ -4,7 +4,7 @@ import { rest } from 'msw';
import { userDetailed } from '../../.storybook/fakes'; import { userDetailed } from '../../.storybook/fakes';
import { commonHandlers } from '../../.storybook/mocks'; import { commonHandlers } from '../../.storybook/mocks';
import MkAchievements from './MkAchievements.vue'; import MkAchievements from './MkAchievements.vue';
import { ACHIEVEMENT_TYPES } from '@/scripts/achievements'; import { ACHIEVEMENT_TYPES } from 'misskey-js';
export const Empty = { export const Empty = {
render(args) { render(args) {
return { return {

View File

@ -44,7 +44,7 @@ import * as misskey from 'misskey-js';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements'; import { ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
user: misskey.entities.User; user: misskey.entities.User;
@ -56,12 +56,12 @@ const props = withDefaults(defineProps<{
}); });
let achievements = $ref(); let achievements = $ref();
const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x))); const lockedAchievements = $computed(() => misskey.ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x)));
function fetch() { function fetch() {
os.api('users/achievements', { userId: props.user.id }).then(res => { os.api('users/achievements', { userId: props.user.id }).then(res => {
achievements = []; achievements = [];
for (const t of ACHIEVEMENT_TYPES) { for (const t of misskey.ACHIEVEMENT_TYPES) {
const a = res.find(x => x.name === t); const a = res.find(x => x.name === t);
if (a) achievements.push(a); if (a) achievements.push(a);
} }

View File

@ -38,7 +38,7 @@ import MkSwitch from './MkSwitch.vue';
import MkInfo from './MkInfo.vue'; import MkInfo from './MkInfo.vue';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import { notificationTypes } from '@/const'; import { notificationTypes } from 'misskey-js';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>> type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>

View File

@ -25,7 +25,7 @@ import MkNote from '@/components/MkNote.vue';
import { stream } from '@/stream'; import { stream } from '@/stream';
import { $i } from '@/account'; import { $i } from '@/account';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { notificationTypes } from '@/const'; import { notificationTypes } from 'misskey-js';
const props = defineProps<{ const props = defineProps<{
includeTypes?: typeof notificationTypes[number][]; includeTypes?: typeof notificationTypes[number][];

View File

@ -49,9 +49,6 @@ https://github.com/sindresorhus/file-type/blob/main/core.js
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
*/ */
export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const;
export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
export const ROLE_POLICIES = [ export const ROLE_POLICIES = [
'gtlAvailable', 'gtlAvailable',
'ltlAvailable', 'ltlAvailable',

View File

@ -22,7 +22,7 @@ import MkNotes from '@/components/MkNotes.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
import { notificationTypes } from '@/const'; import { notificationTypes } from 'misskey-js';
let tab = $ref('all'); let tab = $ref('all');
let includeTypes = $ref<string[] | null>(null); let includeTypes = $ref<string[] | null>(null);

View File

@ -35,7 +35,7 @@ import { $i } from '@/account';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
import { notificationTypes } from '@/const'; import { notificationTypes } from 'misskey-js';
let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>(); let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer); let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);

View File

@ -1,83 +1,6 @@
import * as os from '@/os'; import * as os from '@/os';
import { $i } from '@/account'; import { $i } from '@/account';
export const ACHIEVEMENT_TYPES = [
'notes1',
'notes10',
'notes100',
'notes500',
'notes1000',
'notes5000',
'notes10000',
'notes20000',
'notes30000',
'notes40000',
'notes50000',
'notes60000',
'notes70000',
'notes80000',
'notes90000',
'notes100000',
'login3',
'login7',
'login15',
'login30',
'login60',
'login100',
'login200',
'login300',
'login400',
'login500',
'login600',
'login700',
'login800',
'login900',
'login1000',
'passedSinceAccountCreated1',
'passedSinceAccountCreated2',
'passedSinceAccountCreated3',
'loggedInOnBirthday',
'loggedInOnNewYearsDay',
'noteClipped1',
'noteFavorited1',
'myNoteFavorited1',
'profileFilled',
'markedAsCat',
'following1',
'following10',
'following50',
'following100',
'following300',
'followers1',
'followers10',
'followers50',
'followers100',
'followers300',
'followers500',
'followers1000',
'collectAchievements30',
'viewAchievements3min',
'iLoveMisskey',
'foundTreasure',
'client30min',
'client60min',
'noteDeletedWithin1min',
'postedAtLateNight',
'postedAt0min0sec',
'selfQuote',
'htl20npm',
'viewInstanceChart',
'outputHelloWorldOnScratchpad',
'open3windows',
'driveFolderCircularReference',
'reactWithoutRead',
'clickedClickHere',
'justPlainLucky',
'setNameToSyuilo',
'cookieClicked',
'brainDiver',
] as const;
export const ACHIEVEMENT_BADGES = { export const ACHIEVEMENT_BADGES = {
'notes1': { 'notes1': {
img: '/fluent-emoji/1f4dd.png', img: '/fluent-emoji/1f4dd.png',

View File

@ -1,4 +1,5 @@
export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const;
export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
@ -40,3 +41,80 @@ export const permissions = [
'read:gallery-likes', 'read:gallery-likes',
'write:gallery-likes', 'write:gallery-likes',
]; ];
export const ACHIEVEMENT_TYPES = [
'notes1',
'notes10',
'notes100',
'notes500',
'notes1000',
'notes5000',
'notes10000',
'notes20000',
'notes30000',
'notes40000',
'notes50000',
'notes60000',
'notes70000',
'notes80000',
'notes90000',
'notes100000',
'login3',
'login7',
'login15',
'login30',
'login60',
'login100',
'login200',
'login300',
'login400',
'login500',
'login600',
'login700',
'login800',
'login900',
'login1000',
'passedSinceAccountCreated1',
'passedSinceAccountCreated2',
'passedSinceAccountCreated3',
'loggedInOnBirthday',
'loggedInOnNewYearsDay',
'noteClipped1',
'noteFavorited1',
'myNoteFavorited1',
'profileFilled',
'markedAsCat',
'following1',
'following10',
'following50',
'following100',
'following300',
'followers1',
'followers10',
'followers50',
'followers100',
'followers300',
'followers500',
'followers1000',
'collectAchievements30',
'viewAchievements3min',
'iLoveMisskey',
'foundTreasure',
'client30min',
'client60min',
'noteDeletedWithin1min',
'postedAtLateNight',
'postedAt0min0sec',
'selfQuote',
'htl20npm',
'viewInstanceChart',
'outputHelloWorldOnScratchpad',
'open3windows',
'driveFolderCircularReference',
'reactWithoutRead',
'clickedClickHere',
'justPlainLucky',
'setNameToSyuilo',
'cookieClicked',
'brainDiver',
] as const;

View File

@ -14,9 +14,11 @@ export {
export const permissions = consts.permissions; export const permissions = consts.permissions;
export const notificationTypes = consts.notificationTypes; export const notificationTypes = consts.notificationTypes;
export const obsoleteNotificationTypes = consts.obsoleteNotificationTypes;
export const noteVisibilities = consts.noteVisibilities; export const noteVisibilities = consts.noteVisibilities;
export const mutedNoteReasons = consts.mutedNoteReasons; export const mutedNoteReasons = consts.mutedNoteReasons;
export const ffVisibility = consts.ffVisibility; export const ffVisibility = consts.ffVisibility;
export const ACHIEVEMENT_TYPES = consts.ACHIEVEMENT_TYPES;
// api extractor not supported yet // api extractor not supported yet
//export * as api from './api.js'; //export * as api from './api.js';

View File

@ -64,7 +64,7 @@ export const refs = {
EmojiSimple: packedEmojiSimpleSchema, EmojiSimple: packedEmojiSimpleSchema,
EmojiDetailed: packedEmojiDetailedSchema, EmojiDetailed: packedEmojiDetailedSchema,
Flash: packedFlashSchema, Flash: packedFlashSchema,
} as const satisfies { [x: string]: JSONSchema7Definition }; } as const /*satisfies { [x: string]: JSONSchema7Definition }*/;
type Refs = typeof packedAntennaSchema | typeof packedNoteSchema; // TODO: typeof refs[keyof typeof refs]; type Refs = typeof packedAntennaSchema | typeof packedNoteSchema; // TODO: typeof refs[keyof typeof refs];

View File

@ -1,25 +1,24 @@
import type { JSONSchema7Definition } from 'schema-type';
export const packedNoteReactionSchema = { export const packedNoteReactionSchema = {
$id: 'https://misskey-hub.net/api/schemas/NoteReaction',
type: 'object', type: 'object',
properties: { properties: {
id: { id: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
type: 'string',
optional: false, nullable: false,
format: 'id',
example: 'xxxxxxxxxx',
},
createdAt: { createdAt: {
type: 'string', type: 'string',
optional: false, nullable: false,
format: 'date-time', format: 'date-time',
}, },
user: { user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
type: 'object',
optional: false, nullable: false,
ref: 'UserLite',
},
type: { type: {
type: 'string', type: 'string',
optional: false, nullable: false,
}, },
}, },
} as const; required: [
'id',
'createdAt',
'user',
'type',
],
} as const satisfies JSONSchema7Definition;

View File

@ -1,62 +1,117 @@
import { notificationTypes } from '../consts'; import type { JSONSchema7Definition } from 'schema-type';
import { ACHIEVEMENT_TYPES } from '../consts';
export const packedNotificationSchema = { export const packedNotificationSchema = {
$id: 'https://misskey-hub.net/api/schemas/Notification',
type: 'object', type: 'object',
properties: { allOf: [{
id: { type: 'object',
type: 'string', properties: {
optional: false, nullable: false, id: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
format: 'id', createdAt: {
example: 'xxxxxxxxxx', type: 'string',
format: 'date-time',
},
}, },
createdAt: { required: ['id', 'createdAt'],
type: 'string', }, {
optional: false, nullable: false, oneOf: [
format: 'date-time', {
},
type: {
type: 'string',
optional: false, nullable: false,
enum: [...notificationTypes],
},
user: {
type: 'object', type: 'object',
ref: 'UserLite', properties: {
optional: true, nullable: true, type: { const: 'follow' },
}, userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
userId: { user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
type: 'string', },
optional: true, nullable: true, required: ['type', 'userId', 'user'],
format: 'id', }, {
},
note: {
type: 'object', type: 'object',
ref: 'Note', properties: {
optional: true, nullable: true, type: { const: 'mention' },
}, userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
reaction: { user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
type: 'string', note: { $ref: 'https://misskey-hub.net/api/schemas/Note' },
optional: true, nullable: true, },
}, required: ['type', 'userId', 'user', 'note'],
choice: { }, {
type: 'number',
optional: true, nullable: true,
},
invitation: {
type: 'object', type: 'object',
optional: true, nullable: true, properties: {
}, type: { const: 'reply' },
body: { userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
type: 'string', user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
optional: true, nullable: true, note: { $ref: 'https://misskey-hub.net/api/schemas/Note' },
}, },
header: { required: ['type', 'userId', 'user', 'note'],
type: 'string', }, {
optional: true, nullable: true, type: 'object',
}, properties: {
icon: { type: { const: 'renote' },
type: 'string', userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
optional: true, nullable: true, user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
}, note: { $ref: 'https://misskey-hub.net/api/schemas/Note' },
}, },
} as const; required: ['type', 'userId', 'user', 'note'],
}, {
type: 'object',
properties: {
type: { const: 'quote' },
userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
note: { $ref: 'https://misskey-hub.net/api/schemas/Note' },
},
required: ['type', 'userId', 'user', 'note'],
}, {
type: 'object',
properties: {
type: { const: 'reaction' },
reaction: { type: 'string' },
userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
note: { $ref: 'https://misskey-hub.net/api/schemas/Note' },
},
required: ['type', 'reaction', 'userId', 'user', 'note'],
}, {
type: 'object',
properties: {
type: { const: 'pollEnded' },
userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
note: { $ref: 'https://misskey-hub.net/api/schemas/Note' },
},
required: ['type', 'userId', 'user', 'note'],
}, {
type: 'object',
properties: {
type: { const: 'receiveFollowRequest' },
userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
},
required: ['type', 'userId', 'user'],
}, {
type: 'object',
properties: {
type: { const: 'followRequestAccepted' },
userId: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
user: { $ref: 'https://misskey-hub.net/api/schemas/UserLite' },
},
required: ['type', 'userId', 'user'],
}, {
type: 'object',
properties: {
type: { const: 'achievementEarned' },
achievement: { enum: [...ACHIEVEMENT_TYPES] },
},
required: ['type', 'achievement'],
}, {
type: 'object',
properties: {
type: { const: 'app' },
header: { type: ['string', 'null'] },
body: { type: 'string' },
icon: { type: ['string', 'null'] },
},
required: ['type'],
}],
}],
} as const satisfies JSONSchema7Definition;