mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-13 13:37:31 -07:00
[mastodon-client] GET /v2/suggestions
This commit is contained in:
parent
44b72a2ecc
commit
5f0d140bbe
6 changed files with 93 additions and 57 deletions
|
@ -52,6 +52,14 @@ export function unique<T>(xs: T[]): T[] {
|
|||
return [...new Set(xs)];
|
||||
}
|
||||
|
||||
export function uniqBy<T>(a: T[], key: Function): T[] {
|
||||
const seen = new Set<any>();
|
||||
return a.filter(function(item) {
|
||||
const k = key(item);
|
||||
return seen.has(k) ? false : seen.add(k);
|
||||
})
|
||||
}
|
||||
|
||||
export function sum(xs: number[]): number {
|
||||
return xs.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@ export function convertListId(list: MastodonEntity.List) {
|
|||
return simpleConvertId(list);
|
||||
}
|
||||
|
||||
export function convertSuggestionIds(suggestion: MastodonEntity.SuggestedAccount) {
|
||||
suggestion.account = convertAccountId(suggestion.account)
|
||||
return suggestion
|
||||
}
|
||||
|
||||
export function convertNotificationIds(notification: MastodonEntity.Notification) {
|
||||
notification.account = convertAccountId(notification.account);
|
||||
notification.id = convertId(notification.id, IdType.MastodonId);
|
||||
|
|
|
@ -2,9 +2,9 @@ import Router from "@koa/router";
|
|||
import { getClient } from "@/server/api/mastodon/index.js";
|
||||
import { MiscHelpers } from "@/server/api/mastodon/helpers/misc.js";
|
||||
import authenticate from "@/server/api/authenticate.js";
|
||||
import { argsToBools } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { argsToBools, limitToInt } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { Announcements } from "@/models/index.js";
|
||||
import { convertAnnouncementId } from "@/server/api/mastodon/converters.js";
|
||||
import { convertAnnouncementId, convertSuggestionIds } from "@/server/api/mastodon/converters.js";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
|
||||
export function setupEndpointsMisc(router: Router): void {
|
||||
|
@ -109,4 +109,23 @@ export function setupEndpointsMisc(router: Router): void {
|
|||
ctx.body = e.response.data;
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/v2/suggestions", async (ctx) => {
|
||||
try {
|
||||
const auth = await authenticate(ctx.headers.authorization, null);
|
||||
const user = auth[0] ?? undefined;
|
||||
|
||||
if (!user) {
|
||||
ctx.status = 401;
|
||||
return;
|
||||
}
|
||||
|
||||
const args = limitToInt(ctx.query);
|
||||
ctx.body = await MiscHelpers.getFollowSuggestions(user, args.limit)
|
||||
.then(p => p.map(x => convertSuggestionIds(x)));
|
||||
} catch (e: any) {
|
||||
ctx.status = 500;
|
||||
ctx.body = { error: e.message };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { convertAccountId, convertSearchIds, convertStatusIds } from "../convert
|
|||
import authenticate from "@/server/api/authenticate.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { SearchHelpers } from "@/server/api/mastodon/helpers/search.js";
|
||||
import { MiscHelpers } from "@/server/api/mastodon/helpers/misc.js";
|
||||
|
||||
export function setupEndpointsSearch(router: Router): void {
|
||||
router.get("/v1/search", async (ctx) => {
|
||||
|
@ -69,29 +70,6 @@ export function setupEndpointsSearch(router: Router): void {
|
|||
ctx.body = e.response.data;
|
||||
}
|
||||
});
|
||||
router.get("/v2/suggestions", async (ctx) => {
|
||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||
const accessTokens = ctx.headers.authorization;
|
||||
try {
|
||||
const query: any = ctx.query;
|
||||
let data = await getFeaturedUser(
|
||||
BASE_URL,
|
||||
ctx.request.hostname,
|
||||
accessTokens,
|
||||
query.limit || 20,
|
||||
);
|
||||
data = data.map((suggestion) => {
|
||||
suggestion.account = convertAccountId(suggestion.account);
|
||||
return suggestion;
|
||||
});
|
||||
console.log(data);
|
||||
ctx.body = data;
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
ctx.status = 401;
|
||||
ctx.body = e.response.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function getHighlight(
|
||||
|
@ -112,35 +90,4 @@ async function getHighlight(
|
|||
console.log(e.response.data);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function getFeaturedUser(
|
||||
BASE_URL: string,
|
||||
host: string,
|
||||
accessTokens: string | undefined,
|
||||
limit: number,
|
||||
) {
|
||||
const accessTokenArr = accessTokens?.split(" ") ?? [null];
|
||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
||||
try {
|
||||
const api = await axios.post(`${BASE_URL}/api/users`, {
|
||||
i: accessToken,
|
||||
limit,
|
||||
origin: "local",
|
||||
sort: "+follower",
|
||||
state: "alive",
|
||||
});
|
||||
const data: MisskeyEntity.UserDetail[] = api.data;
|
||||
console.log(data);
|
||||
return data.map((u) => {
|
||||
return {
|
||||
source: "past_interactions",
|
||||
account: new Converter(BASE_URL).userDetail(u, host),
|
||||
};
|
||||
});
|
||||
} catch (e: any) {
|
||||
console.log(e);
|
||||
console.log(e.response.data);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,4 +29,9 @@ namespace MastodonEntity {
|
|||
export type MutedAccount = Account | {
|
||||
mute_expires_at: string | null;
|
||||
}
|
||||
|
||||
export type SuggestedAccount = {
|
||||
source: "staff" | "past_interactions" | "global",
|
||||
account: Account
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@ import { Announcement } from "@/models/entities/announcement.js";
|
|||
import { ILocalUser } from "@/models/entities/user.js";
|
||||
import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import * as Acct from "@/misc/acct.js";
|
||||
import { User } from "@/models/entities/user.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
import { generateMutedUserQueryForUsers } from "@/server/api/common/generate-muted-user-query.js";
|
||||
import { generateBlockQueryForUsers } from "@/server/api/common/generate-block-query.js";
|
||||
import { uniqBy } from "@/prelude/array.js";
|
||||
|
||||
export class MiscHelpers {
|
||||
public static async getInstance(): Promise<MastodonEntity.Instance> {
|
||||
|
@ -123,4 +129,50 @@ export class MiscHelpers {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static async getFollowSuggestions(user: ILocalUser, limit: number): Promise<MastodonEntity.SuggestedAccount[]> {
|
||||
const cache = UserHelpers.getFreshAccountCache();
|
||||
const results: Promise<MastodonEntity.SuggestedAccount[]>[] = [];
|
||||
|
||||
const pinned = fetchMeta().then(meta => Promise.all(
|
||||
meta.pinnedUsers
|
||||
.map((acct) => Acct.parse(acct))
|
||||
.map((acct) =>
|
||||
Users.findOneBy({
|
||||
usernameLower: acct.username.toLowerCase(),
|
||||
host: acct.host ?? IsNull(),
|
||||
}))
|
||||
)
|
||||
.then(p => p.filter(x => !!x) as User[])
|
||||
.then(p => UserConverter.encodeMany(p, cache))
|
||||
.then(p => p.map(x => {
|
||||
return {source: "staff", account: x} as MastodonEntity.SuggestedAccount
|
||||
}))
|
||||
);
|
||||
|
||||
const query = Users.createQueryBuilder("user")
|
||||
.where("user.isExplorable = TRUE")
|
||||
.andWhere("user.host IS NULL")
|
||||
.orderBy("user.followersCount", "DESC")
|
||||
.andWhere("user.updatedAt > :date", {
|
||||
date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5),
|
||||
});
|
||||
|
||||
generateMutedUserQueryForUsers(query, user);
|
||||
generateBlockQueryForUsers(query, user);
|
||||
|
||||
const global = query
|
||||
.take(limit)
|
||||
.getMany()
|
||||
.then(p => UserConverter.encodeMany(p, cache))
|
||||
.then(p => p.map(x => {
|
||||
return {source: "global", account: x} as MastodonEntity.SuggestedAccount
|
||||
}));
|
||||
|
||||
results.push(pinned);
|
||||
results.push(global);
|
||||
|
||||
|
||||
return Promise.all(results).then(p => uniqBy(p.flat(), (x: MastodonEntity.SuggestedAccount) => x.account.id).slice(0, limit));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue