Merge pull request '[PR]: [mastodon-client] More improvements' (#10435) from e2net/calckey:masto-client-improvements into develop

Reviewed-on: https://codeberg.org/calckey/calckey/pulls/10435
This commit is contained in:
Kainoa Kanter 2023-07-08 02:14:50 +00:00
commit 5c42a1b1fe
6 changed files with 84 additions and 23 deletions

View File

@ -10,7 +10,7 @@ import type { Note } from "@/models/entities/note.js";
import type { CacheableLocalUser, User } from "@/models/entities/user.js"; import type { CacheableLocalUser, User } from "@/models/entities/user.js";
import { isActor, isPost, getApId } from "@/remote/activitypub/type.js"; import { isActor, isPost, getApId } from "@/remote/activitypub/type.js";
import type { SchemaType } from "@/misc/schema.js"; import type { SchemaType } from "@/misc/schema.js";
import { HOUR } from "@/const.js"; import { MINUTE } from "@/const.js";
import { shouldBlockInstance } from "@/misc/should-block-instance.js"; import { shouldBlockInstance } from "@/misc/should-block-instance.js";
import { updateQuestion } from "@/remote/activitypub/models/question.js"; import { updateQuestion } from "@/remote/activitypub/models/question.js";
import { populatePoll } from "@/models/repositories/note.js"; import { populatePoll } from "@/models/repositories/note.js";
@ -22,8 +22,8 @@ export const meta = {
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: HOUR, duration: MINUTE,
max: 30, max: 10,
}, },
errors: { errors: {

View File

@ -42,6 +42,7 @@ export function apiSearchMastodon(router: Router): void {
!type || type === "hashtags" !type || type === "hashtags"
? await client.search(query.q, "hashtags", query) ? await client.search(query.q, "hashtags", query)
: null; : null;
ctx.body = { ctx.body = {
accounts: accounts:
acct?.data?.accounts.map((account) => convertAccount(account)) ?? [], acct?.data?.accounts.map((account) => convertAccount(account)) ?? [],

View File

@ -9,5 +9,6 @@ namespace Entity {
votes_count: number votes_count: number
options: Array<PollOption> options: Array<PollOption>
voted: boolean voted: boolean
own_votes: Array<number>
} }
} }

View File

@ -841,7 +841,7 @@ export interface MegalodonInterface {
* @param choices Array of own votes containing index for each option (starting from 0). * @param choices Array of own votes containing index for each option (starting from 0).
* @return Poll * @return Poll
*/ */
votePoll(id: string, choices: Array<number>, status_id?: string | null): Promise<Response<Entity.Poll>> votePoll(id: string, choices: Array<number>): Promise<Response<Entity.Poll>>
// ====================================== // ======================================
// statuses/scheduled_statuses // statuses/scheduled_statuses
// ====================================== // ======================================

View File

@ -1281,6 +1281,8 @@ export default class Misskey implements MegalodonInterface {
status.mentions = (await this.getMentions(status.plain_content!, cache)).filter(p => p != null); status.mentions = (await this.getMentions(status.plain_content!, cache)).filter(p => p != null);
for (const m of status.mentions.filter((value, index, array) => array.indexOf(value) === index)) { for (const m of status.mentions.filter((value, index, array) => array.indexOf(value) === index)) {
if (m.acct == m.username)
status.content = status.content.replace(`@${m.acct}@${this.baseUrlToHost(this.baseUrl)}`, `@${m.acct}`);
status.content = status.content.replace(`@${m.acct}`, `<a href="${m.url}" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@${m.acct}</a>`); status.content = status.content.replace(`@${m.acct}`, `<a href="${m.url}" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@${m.acct}</a>`);
} }
return status; return status;
@ -1686,31 +1688,35 @@ export default class Misskey implements MegalodonInterface {
// ====================================== // ======================================
// statuses/polls // statuses/polls
// ====================================== // ======================================
public async getPoll(_id: string): Promise<Response<Entity.Poll>> { public async getPoll(id: string): Promise<Response<Entity.Poll>> {
return new Promise((_, reject) => { const res = await this.getStatus(id);
const err = new NoImplementedError('misskey does not support') if (res.data.poll == null)
reject(err) throw new Error('poll not found');
}) return { ...res, data: res.data.poll }
} }
/** /**
* POST /api/notes/polls/vote * POST /api/notes/polls/vote
*/ */
public async votePoll(_id: string, choices: Array<number>, status_id?: string | null): Promise<Response<Entity.Poll>> { public async votePoll(id: string, choices: Array<number>): Promise<Response<Entity.Poll>> {
if (!status_id) { if (!id) {
return new Promise((_, reject) => { return new Promise((_, reject) => {
const err = new ArgumentError('status_id is required') const err = new ArgumentError('id is required')
reject(err) reject(err)
}) })
} }
for (const c of choices) {
const params = { const params = {
noteId: status_id, noteId: id,
choice: choices[0] choice: +c
} }
await this.client.post<{}>('/api/notes/polls/vote', params) await this.client.post<{}>('/api/notes/polls/vote', params)
}
const res = await this.client const res = await this.client
.post<MisskeyAPI.Entity.Note>('/api/notes/show', { .post<MisskeyAPI.Entity.Note>('/api/notes/show', {
noteId: status_id noteId: id
}) })
.then(async res => { .then(async res => {
const note = await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache()) const note = await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())
@ -2389,6 +2395,32 @@ export default class Misskey implements MegalodonInterface {
switch (type) { switch (type) {
case 'accounts': { case 'accounts': {
if (q.startsWith("http://") || q.startsWith("https://")) {
return this.client.post('/api/ap/show', {uri: q}).then(async res => {
if (res.status != 200 || res.data.type != 'User') {
res.status = 200;
res.statusText = "OK";
res.data = {
accounts: [],
statuses: [],
hashtags: []
};
return res;
}
const account = await this.converter.userDetail(res.data.object as MisskeyAPI.Entity.UserDetail, this.baseUrlToHost(this.baseUrl));
return {
...res,
data: {
accounts: options?.max_id && options?.max_id >= account.id ? [] : [account],
statuses: [],
hashtags: []
}
};
})
}
let params = { let params = {
query: q query: q
} }
@ -2462,6 +2494,32 @@ export default class Misskey implements MegalodonInterface {
})) }))
} }
case 'statuses': { case 'statuses': {
if (q.startsWith("http://") || q.startsWith("https://")) {
return this.client.post('/api/ap/show', {uri: q}).then(async res => {
if (res.status != 200 || res.data.type != 'Note') {
res.status = 200;
res.statusText = "OK";
res.data = {
accounts: [],
statuses: [],
hashtags: []
};
return res;
}
const post = await this.noteWithDetails(res.data.object as MisskeyAPI.Entity.Note, this.baseUrlToHost(this.baseUrl), accountCache);
return {
...res,
data: {
accounts: [],
statuses: options?.max_id && options.max_id >= post.id ? [] : [post],
hashtags: []
}
}
})
}
let params = { let params = {
query: q query: q
} }

View File

@ -161,7 +161,7 @@ namespace MisskeyAPI {
followers_count: u.followersCount, followers_count: u.followersCount,
following_count: u.followingCount, following_count: u.followingCount,
statuses_count: u.notesCount, statuses_count: u.notesCount,
note: u.description, note: u.description?.replace(/\n|\\n/g, '<br>') ?? '',
url: acctUrl, url: acctUrl,
avatar: u.avatarUrl, avatar: u.avatarUrl,
avatar_static: u.avatarUrl, avatar_static: u.avatarUrl,
@ -275,18 +275,19 @@ namespace MisskeyAPI {
} }
} }
poll = (p: Entity.Poll): MegalodonEntity.Poll => { poll = (p: Entity.Poll, id: string): MegalodonEntity.Poll => {
const now = dayjs() const now = dayjs()
const expire = dayjs(p.expiresAt) const expire = dayjs(p.expiresAt)
const count = p.choices.reduce((sum, choice) => sum + choice.votes, 0) const count = p.choices.reduce((sum, choice) => sum + choice.votes, 0)
return { return {
id: '', id: id,
expires_at: p.expiresAt, expires_at: p.expiresAt,
expired: now.isAfter(expire), expired: now.isAfter(expire),
multiple: p.multiple, multiple: p.multiple,
votes_count: count, votes_count: count,
options: p.choices.map(c => this.choice(c)), options: p.choices.map(c => this.choice(c)),
voted: p.choices.some(c => c.isVoted) voted: p.choices.some(c => c.isVoted),
own_votes: p.choices.filter(c => c.isVoted).map(c => p.choices.indexOf(c))
} }
} }
@ -318,7 +319,7 @@ namespace MisskeyAPI {
mentions: [], mentions: [],
tags: [], tags: [],
card: null, card: null,
poll: n.poll ? this.poll(n.poll) : null, poll: n.poll ? this.poll(n.poll, n.id) : null,
application: null, application: null,
language: null, language: null,
pinned: null, pinned: null,