server: parse quote tag syntax
Some checks failed
ci/woodpecker/push/lint-foundkey-js Pipeline was successful
ci/woodpecker/push/lint-backend Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/lint-client Pipeline was successful
ci/woodpecker/push/lint-sw Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/pr/lint-foundkey-js Pipeline was successful
ci/woodpecker/pr/lint-client Pipeline failed
ci/woodpecker/pr/lint-backend Pipeline failed
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/lint-sw Pipeline failed
ci/woodpecker/pr/test Pipeline failed
Some checks failed
ci/woodpecker/push/lint-foundkey-js Pipeline was successful
ci/woodpecker/push/lint-backend Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/lint-client Pipeline was successful
ci/woodpecker/push/lint-sw Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/pr/lint-foundkey-js Pipeline was successful
ci/woodpecker/pr/lint-client Pipeline failed
ci/woodpecker/pr/lint-backend Pipeline failed
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/lint-sw Pipeline failed
ci/woodpecker/pr/test Pipeline failed
Ref: FEP-e232
This commit is contained in:
parent
dd16f75cae
commit
524352ae87
3 changed files with 65 additions and 8 deletions
|
@ -24,7 +24,7 @@ import { DbResolver } from '../db-resolver.js';
|
|||
import { apLogger } from '../logger.js';
|
||||
import { resolvePerson } from './person.js';
|
||||
import { resolveImage } from './image.js';
|
||||
import { extractApHashtags } from './tag.js';
|
||||
import { extractApHashtags, extractQuoteUrl } from './tag.js';
|
||||
import { extractPollFromQuestion } from './question.js';
|
||||
import { extractApMentions } from './mention.js';
|
||||
|
||||
|
@ -154,10 +154,10 @@ export async function createNote(value: string | IObject, resolver: Resolver, si
|
|||
})
|
||||
: null;
|
||||
|
||||
// 引用
|
||||
let quote: Note | undefined | null;
|
||||
const quoteUrl = extractQuoteUrl(note.tag);
|
||||
|
||||
if (note._misskey_quote || note.quoteUri) {
|
||||
if (quoteUrl || note._misskey_quote || note.quoteUri) {
|
||||
const tryResolveNote = async (uri: string): Promise<{
|
||||
status: 'ok';
|
||||
res: Note | null;
|
||||
|
@ -184,10 +184,16 @@ export async function createNote(value: string | IObject, resolver: Resolver, si
|
|||
}
|
||||
};
|
||||
|
||||
const uris = unique([note._misskey_quote, note.quoteUri].filter((x): x is string => typeof x === 'string'));
|
||||
const results = await Promise.all(uris.map(uri => tryResolveNote(uri)));
|
||||
|
||||
quote = results.filter((x): x is { status: 'ok', res: Note | null } => x.status === 'ok').map(x => x.res).find(x => x);
|
||||
const uris = unique([quoteUrl, note._misskey_quote, note.quoteUri].filter((x): x is string => typeof x === 'string'));
|
||||
// check the urls sequentially and abort early to not do unnecessary HTTP requests
|
||||
// picks the first one that works
|
||||
for (const uri in uris) {
|
||||
const res = await tryResolveNote(uri);
|
||||
if (res.status === 'ok') {
|
||||
quote = res.res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!quote) {
|
||||
if (results.some(x => x.status === 'temperror')) {
|
||||
throw new Error('quote resolve failed');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { toArray } from '@/prelude/array.js';
|
||||
import { IObject, isHashtag, IApHashtag } from '../type.js';
|
||||
import { IObject, isHashtag, IApHashtag, isLink, ILink } from '../type.js';
|
||||
|
||||
export function extractApHashtags(tags: IObject | IObject[] | null | undefined) {
|
||||
if (tags == null) return [];
|
||||
|
@ -16,3 +16,39 @@ export function extractApHashtagObjects(tags: IObject | IObject[] | null | undef
|
|||
if (tags == null) return [];
|
||||
return toArray(tags).filter(isHashtag);
|
||||
}
|
||||
|
||||
// implements FEP-e232: Object Links (2022-12-23 version)
|
||||
export function extractQuoteUrl(tags: IObject | IObject[] | null | undefined): string | null {
|
||||
if (tags == null) return null;
|
||||
|
||||
// filter out correct links
|
||||
let quotes: ILink[] = toArray(tags)
|
||||
.filter(isLink)
|
||||
.filter(link =>
|
||||
[
|
||||
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||
'application/activity+json'
|
||||
].includes(link.mediaType?.toLowerCase())
|
||||
);
|
||||
|
||||
// sort quotes with the right rel first
|
||||
function hasRel(link: ILink): boolean {
|
||||
link.rel != null
|
||||
&&
|
||||
toArray(link.rel)
|
||||
.includes('https://misskey-hub.net/ns#_misskey_quote')
|
||||
}
|
||||
quotes.sort((a, b) => {
|
||||
return hasRel(b) - hasRel(a);
|
||||
});
|
||||
|
||||
// deduplicate by href
|
||||
quotes = quotes.filter((x, i, arr) => arr.findIndex(y => x.href === y.href) === i);
|
||||
|
||||
if (quotes.length === 0) return null;
|
||||
|
||||
// If there is more than one quote, we just pick the first/a random one.
|
||||
// Note that links with the correct `rel` were sorted to the front above
|
||||
// so they will be preferred.
|
||||
return quotes[0];
|
||||
}
|
||||
|
|
|
@ -279,6 +279,13 @@ export interface IFlag extends IActivity {
|
|||
type: 'Flag';
|
||||
}
|
||||
|
||||
export interface ILink extends IObject {
|
||||
type: 'Link';
|
||||
href: string;
|
||||
rel?: string | string[];
|
||||
mediaType?: string;
|
||||
}
|
||||
|
||||
export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create';
|
||||
export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete';
|
||||
export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update';
|
||||
|
@ -293,3 +300,11 @@ export const isLike = (object: IObject): object is ILike => getApType(object) ==
|
|||
export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce';
|
||||
export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block';
|
||||
export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag';
|
||||
export const isLink = (object: IObject): object is ILink => getApType(object) === 'Link'
|
||||
&& typeof object.href === 'string'
|
||||
&& (
|
||||
object.rel == undefined
|
||||
|| typeof object.rel === 'string'
|
||||
|| (Array.isArray(object.rel) && object.rel.every(x => typeof x === 'string'))
|
||||
)
|
||||
&& (object.mediaType == undefined || typeof object.mediaType === 'string');
|
||||
|
|
Loading…
Reference in a new issue