[mastodon-client] POST /accounts/:id/follow, POST /accounts/:id/unfollow
This commit is contained in:
parent
cfc3db5c7c
commit
5c999a20d0
3 changed files with 79 additions and 21 deletions
|
|
@ -8,7 +8,6 @@ import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||||
import authenticate from "@/server/api/authenticate.js";
|
import authenticate from "@/server/api/authenticate.js";
|
||||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||||
import config from "@/config/index.js";
|
|
||||||
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
||||||
|
|
||||||
const relationshipModel = {
|
const relationshipModel = {
|
||||||
|
|
@ -254,16 +253,19 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
router.post<{ Params: { id: string } }>(
|
router.post<{ Params: { id: string } }>(
|
||||||
"/v1/accounts/:id/follow",
|
"/v1/accounts/:id/follow",
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
try {
|
||||||
const data = await client.followAccount(
|
const auth = await authenticate(ctx.headers.authorization, null);
|
||||||
convertId(ctx.params.id, IdType.IceshrimpId),
|
const user = auth[0] ?? null;
|
||||||
);
|
|
||||||
let acct = convertRelationship(data.data);
|
if (!user) {
|
||||||
acct.following = true;
|
ctx.status = 401;
|
||||||
ctx.body = acct;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = await UserHelpers.getUserCached(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||||
|
//FIXME: Parse form data
|
||||||
|
const result = await UserHelpers.followUser(target, user, true, false);
|
||||||
|
ctx.body = convertRelationship(result);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
console.error(e.response.data);
|
console.error(e.response.data);
|
||||||
|
|
@ -275,16 +277,18 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
router.post<{ Params: { id: string } }>(
|
router.post<{ Params: { id: string } }>(
|
||||||
"/v1/accounts/:id/unfollow",
|
"/v1/accounts/:id/unfollow",
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
try {
|
||||||
const data = await client.unfollowAccount(
|
const auth = await authenticate(ctx.headers.authorization, null);
|
||||||
convertId(ctx.params.id, IdType.IceshrimpId),
|
const user = auth[0] ?? null;
|
||||||
);
|
|
||||||
let acct = convertRelationship(data.data);
|
if (!user) {
|
||||||
acct.following = false;
|
ctx.status = 401;
|
||||||
ctx.body = acct;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = await UserHelpers.getUserCached(convertId(ctx.params.id, IdType.IceshrimpId));
|
||||||
|
const result = await UserHelpers.unfollowUser(target, user);
|
||||||
|
ctx.body = convertRelationship(result);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
console.error(e.response.data);
|
console.error(e.response.data);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ namespace MastodonEntity {
|
||||||
id: string;
|
id: string;
|
||||||
following: boolean;
|
following: boolean;
|
||||||
followed_by: boolean;
|
followed_by: boolean;
|
||||||
delivery_following?: boolean;
|
|
||||||
blocking: boolean;
|
blocking: boolean;
|
||||||
blocked_by: boolean;
|
blocked_by: boolean;
|
||||||
muting: boolean;
|
muting: boolean;
|
||||||
|
|
@ -13,5 +12,6 @@ namespace MastodonEntity {
|
||||||
showing_reblogs: boolean;
|
showing_reblogs: boolean;
|
||||||
endorsed: boolean;
|
endorsed: boolean;
|
||||||
notifying: boolean;
|
notifying: boolean;
|
||||||
|
note: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
import { ILocalUser, User } from "@/models/entities/user.js";
|
import { ILocalUser, User } from "@/models/entities/user.js";
|
||||||
import { Followings, NoteFavorites, NoteReactions, Notes, UserProfiles } from "@/models/index.js";
|
import {
|
||||||
|
Followings,
|
||||||
|
FollowRequests,
|
||||||
|
NoteFavorites,
|
||||||
|
NoteReactions,
|
||||||
|
Notes,
|
||||||
|
UserProfiles,
|
||||||
|
Users
|
||||||
|
} from "@/models/index.js";
|
||||||
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||||
import { generateRepliesQuery } from "@/server/api/common/generate-replies-query.js";
|
import { generateRepliesQuery } from "@/server/api/common/generate-replies-query.js";
|
||||||
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
||||||
|
|
@ -11,6 +19,10 @@ import Entity from "megalodon/src/entity.js";
|
||||||
import AsyncLock from "async-lock";
|
import AsyncLock from "async-lock";
|
||||||
import { getUser } from "@/server/api/common/getters.js";
|
import { getUser } from "@/server/api/common/getters.js";
|
||||||
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
||||||
|
import { awaitAll } from "@/prelude/await-all.js";
|
||||||
|
import createFollowing from "@/services/following/create.js";
|
||||||
|
import deleteFollowing from "@/services/following/delete.js";
|
||||||
|
import cancelFollowRequest from "@/services/following/requests/cancel.js";
|
||||||
|
|
||||||
export type AccountCache = {
|
export type AccountCache = {
|
||||||
locks: AsyncLock;
|
locks: AsyncLock;
|
||||||
|
|
@ -27,6 +39,27 @@ export type LinkPaginationObject<T> = {
|
||||||
type RelationshipType = 'followers' | 'following';
|
type RelationshipType = 'followers' | 'following';
|
||||||
|
|
||||||
export class UserHelpers {
|
export class UserHelpers {
|
||||||
|
public static async followUser(target: User, localUser: ILocalUser, reblogs: boolean, notify: boolean) {
|
||||||
|
//FIXME: implement reblogs & notify params
|
||||||
|
const following = await Followings.exist({where: {followerId: localUser.id, followeeId: target.id}});
|
||||||
|
const requested = await FollowRequests.exist({where: {followerId: localUser.id, followeeId: target.id}});
|
||||||
|
if (!following && !requested)
|
||||||
|
await createFollowing(localUser, target);
|
||||||
|
|
||||||
|
return this.getUserRelationshipTo(target, localUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async unfollowUser(target: User, localUser: ILocalUser) {
|
||||||
|
const following = await Followings.exist({where: {followerId: localUser.id, followeeId: target.id}});
|
||||||
|
const requested = await FollowRequests.exist({where: {followerId: localUser.id, followeeId: target.id}});
|
||||||
|
if (following)
|
||||||
|
await deleteFollowing(localUser, target);
|
||||||
|
if (requested)
|
||||||
|
await cancelFollowRequest(target, localUser);
|
||||||
|
|
||||||
|
return this.getUserRelationshipTo(target, localUser);
|
||||||
|
}
|
||||||
|
|
||||||
public static async getUserStatuses(user: User, localUser: ILocalUser | null, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20, onlyMedia: boolean = false, excludeReplies: boolean = false, excludeReblogs: boolean = false, pinned: boolean = false, tagged: string | undefined): Promise<Note[]> {
|
public static async getUserStatuses(user: User, localUser: ILocalUser | null, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20, onlyMedia: boolean = false, excludeReplies: boolean = false, excludeReblogs: boolean = false, pinned: boolean = false, tagged: string | undefined): Promise<Note[]> {
|
||||||
if (limit > 40) limit = 40;
|
if (limit > 40) limit = 40;
|
||||||
|
|
||||||
|
|
@ -172,6 +205,27 @@ export class UserHelpers {
|
||||||
return this.getUserRelationships('following', user, localUser, maxId, sinceId, minId, limit);
|
return this.getUserRelationships('following', user, localUser, maxId, sinceId, minId, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async getUserRelationshipTo(target: User, localUser: ILocalUser): Promise<MastodonEntity.Relationship> {
|
||||||
|
const relation = await Users.getRelation(localUser.id, target.id);
|
||||||
|
const response = {
|
||||||
|
id: target.id,
|
||||||
|
following: relation.isFollowing,
|
||||||
|
followed_by: relation.isFollowed,
|
||||||
|
blocking: relation.isBlocking,
|
||||||
|
blocked_by: relation.isBlocked,
|
||||||
|
muting: relation.isMuted,
|
||||||
|
muting_notifications: relation.isMuted,
|
||||||
|
requested: relation.hasPendingFollowRequestFromYou,
|
||||||
|
domain_blocking: false, //FIXME
|
||||||
|
showing_reblogs: !relation.isRenoteMuted,
|
||||||
|
endorsed: false,
|
||||||
|
notifying: false, //FIXME
|
||||||
|
note: '' //FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
return awaitAll(response);
|
||||||
|
}
|
||||||
|
|
||||||
public static async getUserCached(id: string, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<User> {
|
public static async getUserCached(id: string, cache: AccountCache = UserHelpers.getFreshAccountCache()): Promise<User> {
|
||||||
return cache.locks.acquire(id, async () => {
|
return cache.locks.acquire(id, async () => {
|
||||||
const cacheHit = cache.users.find(p => p.id == id);
|
const cacheHit = cache.users.find(p => p.id == id);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue