Fixed 267U.pre2

This commit is contained in:
2026-07-26 18:25:37 +09:00
parent 50bfaeafdf
commit 317d00a284
1286 changed files with 80222 additions and 1 deletions
@@ -0,0 +1,46 @@
import { UserGroups, UserGroupJoinings } from "../../../../../models/index.js";
import { genId } from "../../../../../misc/gen-id.js";
import define from "../../../define.js";
export const meta = {
tags: [
"groups"
],
requireCredential: true,
kind: "write:user-groups",
description: "Create a new group.",
res: {
type: "object",
optional: false,
nullable: false,
ref: "UserGroup"
}
};
export const paramDef = {
type: "object",
properties: {
name: {
type: "string",
minLength: 1,
maxLength: 100
}
},
required: [
"name"
]
};
export default define(meta, paramDef, async (ps, user)=>{
const userGroup = await UserGroups.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
name: ps.name
}).then((x)=>UserGroups.findOneByOrFail(x.identifiers[0]));
// Push the owner
await UserGroupJoinings.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
userGroupId: userGroup.id
});
return await UserGroups.pack(userGroup);
});
@@ -0,0 +1,40 @@
import { UserGroups } from "../../../../../models/index.js";
import define from "../../../define.js";
import { ApiError } from "../../../error.js";
export const meta = {
tags: [
"groups"
],
requireCredential: true,
kind: "write:user-groups",
description: "Delete an existing group.",
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "63dbd64c-cd77-413f-8e08-61781e210b38"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
}
},
required: [
"groupId"
]
};
export default define(meta, paramDef, async (ps, user)=>{
const userGroup = await UserGroups.findOneBy({
id: ps.groupId,
userId: user.id
});
if (userGroup == null) {
throw new ApiError(meta.errors.noSuchGroup);
}
await UserGroups.delete(userGroup.id);
});
@@ -0,0 +1,104 @@
import define from "../../../../define.js";
import { ApiError } from "../../../../error.js";
import { DriveFiles, UserEmojis, UserGroups } from "../../../../../../models/index.js";
import { genId } from "../../../../../../misc/gen-id.js";
import { getEmojiSize } from "../../../../../../misc/emoji-meta.js";
import { clearGroupEmojiCache } from "../../../../../../misc/populate-emojis.js";
function normalizeMimeType(type) {
const mime = type?.split(";")[0]?.trim().toLowerCase();
return mime && mime.length <= 64 ? mime : null;
}
export const meta = {
tags: [
"groups"
],
requireCredential: true,
kind: "write:user-groups",
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "ab752eba-3f77-4746-b805-1457719f648d"
},
noSuchFile: {
message: "No such file.",
code: "NO_SUCH_FILE",
id: "be2812bf-55c0-4fd9-8d1b-813578fc81f9"
},
alreadyExists: {
message: "Group emoji already exists.",
code: "ALREADY_EXISTS",
id: "7957d717-e009-47c8-b32d-37a13afed4db"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
},
name: {
type: "string",
pattern: "^[a-z0-9_]{1,64}$"
},
fileId: {
type: "string",
format: "misskey:id"
},
glyph: {
type: "boolean",
default: false
}
},
required: [
"groupId",
"name",
"fileId"
]
};
export default define(meta, paramDef, async (ps, me)=>{
const group = await UserGroups.findOneBy({
id: ps.groupId,
userId: me.id
});
if (!group) throw new ApiError(meta.errors.noSuchGroup);
const file = await DriveFiles.findOneBy({
id: ps.fileId,
userId: me.id
});
if (!file || !file.type.startsWith("image/")) throw new ApiError(meta.errors.noSuchFile);
const exists = await UserEmojis.findOneBy({
name: ps.name,
userGroupId: group.id
});
if (exists) throw new ApiError(meta.errors.alreadyExists);
const size = await getEmojiSize(file.url).catch(()=>({
width: null,
height: null
}));
const emoji = await UserEmojis.insert({
id: genId(),
createdAt: new Date(),
name: ps.name,
userId: null,
userGroupId: group.id,
originalUrl: file.url,
publicUrl: file.webpublicUrl ?? file.url,
type: normalizeMimeType(file.webpublicType ?? file.type),
glyph: ps.glyph,
width: size.width || null,
height: size.height || null
}).then((x)=>UserEmojis.findOneByOrFail(x.identifiers[0]));
await clearGroupEmojiCache(emoji.name, group.username);
return {
id: emoji.id,
name: emoji.name,
url: emoji.publicUrl || emoji.originalUrl,
glyph: emoji.glyph,
glyphUrl: emoji.glyph ? emoji.originalUrl : null,
width: emoji.width,
height: emoji.height
};
});
@@ -0,0 +1,43 @@
import define from "../../../../define.js";
import { ApiError } from "../../../../error.js";
import { UserEmojis, UserGroups } from "../../../../../../models/index.js";
import { clearGroupEmojiCache } from "../../../../../../misc/populate-emojis.js";
export const meta = {
tags: [
"groups"
],
requireCredential: true,
kind: "write:user-groups",
errors: {
noSuchEmoji: {
message: "No such group emoji.",
code: "NO_SUCH_GROUP_EMOJI",
id: "e22d8bd7-d9c5-4470-8ef0-9a9095985b3f"
}
}
};
export const paramDef = {
type: "object",
properties: {
id: {
type: "string",
format: "misskey:id"
}
},
required: [
"id"
]
};
export default define(meta, paramDef, async (ps, me)=>{
const emoji = await UserEmojis.findOneBy({
id: ps.id
});
if (!emoji?.userGroupId) throw new ApiError(meta.errors.noSuchEmoji);
const group = await UserGroups.findOneBy({
id: emoji.userGroupId,
userId: me.id
});
if (!group) throw new ApiError(meta.errors.noSuchEmoji);
await UserEmojis.delete(emoji.id);
await clearGroupEmojiCache(emoji.name, group.username);
});
@@ -0,0 +1,57 @@
import define from "../../../../define.js";
import { ApiError } from "../../../../error.js";
import { UserEmojis, UserGroupJoinings, UserGroups } from "../../../../../../models/index.js";
export const meta = {
tags: [
"groups"
],
requireCredential: true,
kind: "read:user-groups",
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "e9f78109-710a-44fd-8ab2-51582f65dd97"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
}
},
required: [
"groupId"
]
};
export default define(meta, paramDef, async (ps, me)=>{
const group = await UserGroups.findOneBy({
id: ps.groupId
});
if (!group) throw new ApiError(meta.errors.noSuchGroup);
const member = await UserGroupJoinings.findOneBy({
userGroupId: group.id,
userId: me.id
});
if (group.userId !== me.id && !member) throw new ApiError(meta.errors.noSuchGroup);
const emojis = await UserEmojis.find({
where: {
userGroupId: group.id
},
order: {
createdAt: "DESC"
}
});
return emojis.map((emoji)=>({
id: emoji.id,
name: emoji.name,
url: emoji.publicUrl || emoji.originalUrl,
glyph: emoji.glyph,
glyphUrl: emoji.glyph ? emoji.originalUrl : null,
width: emoji.width,
height: emoji.height
}));
});
@@ -0,0 +1,52 @@
import { UserGroupJoinings, UserGroupInvitations } from "../../../../../../models/index.js";
import { genId } from "../../../../../../misc/gen-id.js";
import { ApiError } from "../../../../error.js";
import define from "../../../../define.js";
export const meta = {
tags: [
"groups",
"users"
],
requireCredential: true,
kind: "write:user-groups",
description: "Join a group the authenticated user has been invited to.",
errors: {
noSuchInvitation: {
message: "No such invitation.",
code: "NO_SUCH_INVITATION",
id: "98c11eca-c890-4f42-9806-c8c8303ebb5e"
}
}
};
export const paramDef = {
type: "object",
properties: {
invitationId: {
type: "string",
format: "misskey:id"
}
},
required: [
"invitationId"
]
};
export default define(meta, paramDef, async (ps, user)=>{
// Fetch the invitation
const invitation = await UserGroupInvitations.findOneBy({
id: ps.invitationId
});
if (invitation == null) {
throw new ApiError(meta.errors.noSuchInvitation);
}
if (invitation.userId !== user.id) {
throw new ApiError(meta.errors.noSuchInvitation);
}
// Push the user
await UserGroupJoinings.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
userGroupId: invitation.userGroupId
});
UserGroupInvitations.delete(invitation.id);
});
@@ -0,0 +1,44 @@
import { UserGroupInvitations } from "../../../../../../models/index.js";
import define from "../../../../define.js";
import { ApiError } from "../../../../error.js";
export const meta = {
tags: [
"groups",
"users"
],
requireCredential: true,
kind: "write:user-groups",
description: "Delete an existing group invitation for the authenticated user without joining the group.",
errors: {
noSuchInvitation: {
message: "No such invitation.",
code: "NO_SUCH_INVITATION",
id: "ad7471d4-2cd9-44b4-ac68-e7136b4ce656"
}
}
};
export const paramDef = {
type: "object",
properties: {
invitationId: {
type: "string",
format: "misskey:id"
}
},
required: [
"invitationId"
]
};
export default define(meta, paramDef, async (ps, user)=>{
// Fetch the invitation
const invitation = await UserGroupInvitations.findOneBy({
id: ps.invitationId
});
if (invitation == null) {
throw new ApiError(meta.errors.noSuchInvitation);
}
if (invitation.userId !== user.id) {
throw new ApiError(meta.errors.noSuchInvitation);
}
await UserGroupInvitations.delete(invitation.id);
});
@@ -0,0 +1,94 @@
import { UserGroups, UserGroupJoinings, UserGroupInvitations } from "../../../../../models/index.js";
import { genId } from "../../../../../misc/gen-id.js";
import { createNotification } from "../../../../../services/create-notification.js";
import { getUser } from "../../../common/getters.js";
import { ApiError } from "../../../error.js";
import define from "../../../define.js";
export const meta = {
tags: [
"groups",
"users"
],
requireCredential: true,
kind: "write:user-groups",
description: "Invite a user to an existing group.",
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "583f8bc0-8eee-4b78-9299-1e14fc91e409"
},
noSuchUser: {
message: "No such user.",
code: "NO_SUCH_USER",
id: "da52de61-002c-475b-90e1-ba64f9cf13a8"
},
alreadyAdded: {
message: "That user has already been added to that group.",
code: "ALREADY_ADDED",
id: "7e35c6a0-39b2-4488-aea6-6ee20bd5da2c"
},
alreadyInvited: {
message: "That user has already been invited to that group.",
code: "ALREADY_INVITED",
id: "ee0f58b4-b529-4d13-b761-b9a3e69f97e6"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
},
userId: {
type: "string",
format: "misskey:id"
}
},
required: [
"groupId",
"userId"
]
};
export default define(meta, paramDef, async (ps, me)=>{
// Fetch the group
const userGroup = await UserGroups.findOneBy({
id: ps.groupId,
userId: me.id
});
if (userGroup == null) {
throw new ApiError(meta.errors.noSuchGroup);
}
// Fetch the user
const user = await getUser(ps.userId).catch((e)=>{
if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") throw new ApiError(meta.errors.noSuchUser);
throw e;
});
const joining = await UserGroupJoinings.findOneBy({
userGroupId: userGroup.id,
userId: user.id
});
if (joining) {
throw new ApiError(meta.errors.alreadyAdded);
}
const existInvitation = await UserGroupInvitations.findOneBy({
userGroupId: userGroup.id,
userId: user.id
});
if (existInvitation) {
throw new ApiError(meta.errors.alreadyInvited);
}
const invitation = await UserGroupInvitations.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
userGroupId: userGroup.id
}).then((x)=>UserGroupInvitations.findOneByOrFail(x.identifiers[0]));
// 通知を作成
createNotification(user.id, "groupInvited", {
notifierId: me.id,
userGroupInvitationId: invitation.id
});
});
@@ -0,0 +1,40 @@
import { Not, In } from "typeorm";
import { UserGroups, UserGroupJoinings } from "../../../../../models/index.js";
import define from "../../../define.js";
export const meta = {
tags: [
"groups",
"account"
],
requireCredential: true,
kind: "read:user-groups",
description: "List the groups that the authenticated user is a member of.",
res: {
type: "array",
optional: false,
nullable: false,
items: {
type: "object",
optional: false,
nullable: false,
ref: "UserGroup"
}
}
};
export const paramDef = {
type: "object",
properties: {},
required: []
};
export default define(meta, paramDef, async (ps, me)=>{
const ownedGroups = await UserGroups.findBy({
userId: me.id
});
const joinings = await UserGroupJoinings.findBy({
userId: me.id,
...ownedGroups.length > 0 ? {
userGroupId: Not(In(ownedGroups.map((x)=>x.id)))
} : {}
});
return await Promise.all(joinings.map((x)=>UserGroups.pack(x.userGroupId)));
});
@@ -0,0 +1,52 @@
import { UserGroups, UserGroupJoinings } from "../../../../../models/index.js";
import define from "../../../define.js";
import { ApiError } from "../../../error.js";
export const meta = {
tags: [
"groups",
"users"
],
requireCredential: true,
kind: "write:user-groups",
description: "Leave a group. The owner of a group can not leave. They must transfer ownership or delete the group instead.",
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "62780270-1f67-5dc0-daca-3eb510612e31"
},
youAreOwner: {
message: "Your are the owner.",
code: "YOU_ARE_OWNER",
id: "b6d6e0c2-ef8a-9bb8-653d-79f4a3107c69"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
}
},
required: [
"groupId"
]
};
export default define(meta, paramDef, async (ps, me)=>{
// Fetch the group
const userGroup = await UserGroups.findOneBy({
id: ps.groupId
});
if (userGroup == null) {
throw new ApiError(meta.errors.noSuchGroup);
}
if (me.id === userGroup.userId) {
throw new ApiError(meta.errors.youAreOwner);
}
await UserGroupJoinings.delete({
userGroupId: userGroup.id,
userId: me.id
});
});
@@ -0,0 +1,33 @@
import { UserGroups } from "../../../../../models/index.js";
import define from "../../../define.js";
export const meta = {
tags: [
"groups",
"account"
],
requireCredential: true,
kind: "read:user-groups",
description: "List the groups that the authenticated user is the owner of.",
res: {
type: "array",
optional: false,
nullable: false,
items: {
type: "object",
optional: false,
nullable: false,
ref: "UserGroup"
}
}
};
export const paramDef = {
type: "object",
properties: {},
required: []
};
export default define(meta, paramDef, async (ps, me)=>{
const userGroups = await UserGroups.findBy({
userId: me.id
});
return await Promise.all(userGroups.map((x)=>UserGroups.pack(x)));
});
@@ -0,0 +1,70 @@
import { UserGroups, UserGroupJoinings } from "../../../../../models/index.js";
import define from "../../../define.js";
import { ApiError } from "../../../error.js";
import { getUser } from "../../../common/getters.js";
export const meta = {
tags: [
"groups",
"users"
],
requireCredential: true,
kind: "write:user-groups",
description: "Removes a specified user from a group. The owner can not be removed.",
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "4662487c-05b1-4b78-86e5-fd46998aba74"
},
noSuchUser: {
message: "No such user.",
code: "NO_SUCH_USER",
id: "0b5cc374-3681-41da-861e-8bc1146f7a55"
},
isOwner: {
message: "The user is the owner.",
code: "IS_OWNER",
id: "1546eed5-4414-4dea-81c1-b0aec4f6d2af"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
},
userId: {
type: "string",
format: "misskey:id"
}
},
required: [
"groupId",
"userId"
]
};
export default define(meta, paramDef, async (ps, me)=>{
// Fetch the group
const userGroup = await UserGroups.findOneBy({
id: ps.groupId,
userId: me.id
});
if (userGroup == null) {
throw new ApiError(meta.errors.noSuchGroup);
}
// Fetch the user
const user = await getUser(ps.userId).catch((e)=>{
if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") throw new ApiError(meta.errors.noSuchUser);
throw e;
});
if (user.id === userGroup.userId) {
throw new ApiError(meta.errors.isOwner);
}
// Pull the user
await UserGroupJoinings.delete({
userGroupId: userGroup.id,
userId: user.id
});
});
@@ -0,0 +1,44 @@
import { UserGroups } from "../../../../../models/index.js";
import define from "../../../define.js";
import { ApiError } from "../../../error.js";
export const meta = {
tags: [
"groups"
],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: "object",
optional: false,
nullable: false,
ref: "UserGroup"
},
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "e2eb1dcc-d778-4a5f-b3c1-f7a0c7c3f9ad"
}
}
};
export const paramDef = {
type: "object",
properties: {
username: {
type: "string",
minLength: 1,
maxLength: 64
}
},
required: [
"username"
]
};
export default define(meta, paramDef, async (ps)=>{
const group = await UserGroups.findOneBy({
username: ps.username.toLowerCase(),
isPrivate: false
});
if (!group) throw new ApiError(meta.errors.noSuchGroup);
return UserGroups.pack(group);
});
@@ -0,0 +1,54 @@
import { UserGroups, UserGroupJoinings } from "../../../../../models/index.js";
import define from "../../../define.js";
import { ApiError } from "../../../error.js";
export const meta = {
tags: [
"groups",
"account"
],
requireCredential: true,
kind: "read:user-groups",
description: "Show the properties of a group.",
res: {
type: "object",
optional: false,
nullable: false,
ref: "UserGroup"
},
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "ea04751e-9b7e-487b-a509-330fb6bd6b9b"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
}
},
required: [
"groupId"
]
};
export default define(meta, paramDef, async (ps, me)=>{
// Fetch the group
const userGroup = await UserGroups.findOneBy({
id: ps.groupId
});
if (userGroup == null) {
throw new ApiError(meta.errors.noSuchGroup);
}
const joining = await UserGroupJoinings.findOneBy({
userId: me.id,
userGroupId: userGroup.id
});
if (joining == null && userGroup.userId !== me.id) {
throw new ApiError(meta.errors.noSuchGroup);
}
return await UserGroups.pack(userGroup);
});
@@ -0,0 +1,79 @@
import { UserGroups, UserGroupJoinings } from "../../../../../models/index.js";
import define from "../../../define.js";
import { ApiError } from "../../../error.js";
import { getUser } from "../../../common/getters.js";
export const meta = {
tags: [
"groups",
"users"
],
requireCredential: true,
kind: "write:user-groups",
description: "Transfer ownership of a group from the authenticated user to another user.",
res: {
type: "object",
optional: false,
nullable: false,
ref: "UserGroup"
},
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "8e31d36b-2f88-4ccd-a438-e2d78a9162db"
},
noSuchUser: {
message: "No such user.",
code: "NO_SUCH_USER",
id: "711f7ebb-bbb9-4dfa-b540-b27809fed5e9"
},
noSuchGroupMember: {
message: "No such group member.",
code: "NO_SUCH_GROUP_MEMBER",
id: "d31bebee-196d-42c2-9a3e-9474d4be6cc4"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
},
userId: {
type: "string",
format: "misskey:id"
}
},
required: [
"groupId",
"userId"
]
};
export default define(meta, paramDef, async (ps, me)=>{
// Fetch the group
const userGroup = await UserGroups.findOneBy({
id: ps.groupId,
userId: me.id
});
if (userGroup == null) {
throw new ApiError(meta.errors.noSuchGroup);
}
// Fetch the user
const user = await getUser(ps.userId).catch((e)=>{
if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") throw new ApiError(meta.errors.noSuchUser);
throw e;
});
const joining = await UserGroupJoinings.findOneBy({
userGroupId: userGroup.id,
userId: user.id
});
if (joining == null) {
throw new ApiError(meta.errors.noSuchGroupMember);
}
await UserGroups.update(userGroup.id, {
userId: ps.userId
});
return await UserGroups.pack(userGroup.id);
});
@@ -0,0 +1,102 @@
import { DriveFiles, UserGroups } from "../../../../../models/index.js";
import define from "../../../define.js";
import { ApiError } from "../../../error.js";
export const meta = {
tags: [
"groups"
],
requireCredential: true,
kind: "write:user-groups",
description: "Update the properties of a group.",
res: {
type: "object",
optional: false,
nullable: false,
ref: "UserGroup"
},
errors: {
noSuchGroup: {
message: "No such group.",
code: "NO_SUCH_GROUP",
id: "9081cda3-7a9e-4fac-a6ce-908d70f282f6"
},
noSuchFile: {
message: "No such file.",
code: "NO_SUCH_FILE",
id: "45861e5e-75d4-4353-b8a9-a72fd2b4c62f"
}
}
};
export const paramDef = {
type: "object",
properties: {
groupId: {
type: "string",
format: "misskey:id"
},
name: {
type: "string",
minLength: 1,
maxLength: 100
},
username: {
type: "string",
pattern: "^[a-zA-Z0-9_]{1,64}$",
nullable: true
},
allowCalls: {
type: "boolean"
},
iconFileId: {
type: "string",
format: "misskey:id",
nullable: true
},
symbolFileId: {
type: "string",
format: "misskey:id",
nullable: true
}
},
required: [
"groupId",
"name"
]
};
export default define(meta, paramDef, async (ps, me)=>{
// Fetch the group
const userGroup = await UserGroups.findOneBy({
id: ps.groupId,
userId: me.id
});
if (userGroup == null) {
throw new ApiError(meta.errors.noSuchGroup);
}
const updates = {
name: ps.name
};
if (ps.username !== undefined) updates.username = ps.username?.toLowerCase() ?? null;
if (typeof ps.allowCalls === "boolean") updates.allowCalls = ps.allowCalls;
if (ps.iconFileId !== undefined) {
if (ps.iconFileId) {
const file = await DriveFiles.findOneBy({
id: ps.iconFileId,
userId: me.id
});
if (!file) throw new ApiError(meta.errors.noSuchFile);
}
updates.iconFileId = ps.iconFileId;
}
if (ps.symbolFileId !== undefined) {
if (ps.symbolFileId) {
const file = await DriveFiles.findOneBy({
id: ps.symbolFileId,
userId: me.id
});
if (!file) throw new ApiError(meta.errors.noSuchFile);
}
updates.symbolFileId = ps.symbolFileId;
}
await UserGroups.update(userGroup.id, updates);
return await UserGroups.pack(userGroup.id);
});