mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-07-13 07:24:33 +00:00
support reactions in mastodon API
This commit is contained in:
parent
fbdee815da
commit
3c54680860
8 changed files with 82 additions and 54 deletions
|
@ -180,10 +180,10 @@ export class MastoConverters {
|
||||||
note: profile?.description ?? '',
|
note: profile?.description ?? '',
|
||||||
url: user.uri ?? acctUrl,
|
url: user.uri ?? acctUrl,
|
||||||
uri: user.uri ?? acctUri,
|
uri: user.uri ?? acctUri,
|
||||||
avatar: user.avatarUrl ? user.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
avatar: user.avatarUrl ?? 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||||
avatar_static: user.avatarUrl ? user.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
avatar_static: user.avatarUrl ?? 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||||
header: user.bannerUrl ? user.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
header: user.bannerUrl ?? 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||||
header_static: user.bannerUrl ? user.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
header_static: user.bannerUrl ?? 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||||
emojis: emoji,
|
emojis: emoji,
|
||||||
moved: null, //FIXME
|
moved: null, //FIXME
|
||||||
fields: profile?.fields.map(p => this.encodeField(p)) ?? [],
|
fields: profile?.fields.map(p => this.encodeField(p)) ?? [],
|
||||||
|
@ -196,7 +196,7 @@ export class MastoConverters {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getEdits(id: string, me?: MiLocalUser | null): Promise<StatusEdit[]> {
|
public async getEdits(id: string, me: MiLocalUser | null): Promise<StatusEdit[]> {
|
||||||
const note = await this.mastodonDataService.getNote(id, me);
|
const note = await this.mastodonDataService.getNote(id, me);
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -213,7 +213,7 @@ export class MastoConverters {
|
||||||
account: noteUser,
|
account: noteUser,
|
||||||
content: this.mfmService.toMastoApiHtml(mfm.parse(edit.newText ?? ''), JSON.parse(note.mentionedRemoteUsers)) ?? '',
|
content: this.mfmService.toMastoApiHtml(mfm.parse(edit.newText ?? ''), JSON.parse(note.mentionedRemoteUsers)) ?? '',
|
||||||
created_at: lastDate.toISOString(),
|
created_at: lastDate.toISOString(),
|
||||||
emojis: [],
|
emojis: [], //FIXME
|
||||||
sensitive: edit.cw != null && edit.cw.length > 0,
|
sensitive: edit.cw != null && edit.cw.length > 0,
|
||||||
spoiler_text: edit.cw ?? '',
|
spoiler_text: edit.cw ?? '',
|
||||||
media_attachments: files.length > 0 ? files.map((f) => this.encodeFile(f)) : [],
|
media_attachments: files.length > 0 ? files.map((f) => this.encodeFile(f)) : [],
|
||||||
|
@ -222,15 +222,15 @@ export class MastoConverters {
|
||||||
history.push(item);
|
history.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Promise.all(history);
|
return history;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async convertReblog(status: Entity.Status | null, me?: MiLocalUser | null): Promise<MastodonEntity.Status | null> {
|
private async convertReblog(status: Entity.Status | null, me: MiLocalUser | null): Promise<MastodonEntity.Status | null> {
|
||||||
if (!status) return null;
|
if (!status) return null;
|
||||||
return await this.convertStatus(status, me);
|
return await this.convertStatus(status, me);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convertStatus(status: Entity.Status, me?: MiLocalUser | null): Promise<MastodonEntity.Status> {
|
public async convertStatus(status: Entity.Status, me: MiLocalUser | null): Promise<MastodonEntity.Status> {
|
||||||
const convertedAccount = this.convertAccount(status.account);
|
const convertedAccount = this.convertAccount(status.account);
|
||||||
const note = await this.mastodonDataService.requireNote(status.id, me);
|
const note = await this.mastodonDataService.requireNote(status.id, me);
|
||||||
const noteUser = await this.getUser(status.account.id);
|
const noteUser = await this.getUser(status.account.id);
|
||||||
|
@ -279,7 +279,6 @@ export class MastoConverters {
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const reblogged = await this.mastodonDataService.hasReblog(note.id, me);
|
const reblogged = await this.mastodonDataService.hasReblog(note.id, me);
|
||||||
const reactions = await Promise.all(status.emoji_reactions.map(r => this.convertReaction(r)));
|
|
||||||
|
|
||||||
// noinspection ES6MissingAwait
|
// noinspection ES6MissingAwait
|
||||||
return await awaitAll({
|
return await awaitAll({
|
||||||
|
@ -289,11 +288,12 @@ export class MastoConverters {
|
||||||
account: convertedAccount,
|
account: convertedAccount,
|
||||||
in_reply_to_id: note.replyId,
|
in_reply_to_id: note.replyId,
|
||||||
in_reply_to_account_id: note.replyUserId,
|
in_reply_to_account_id: note.replyUserId,
|
||||||
reblog: !isQuote ? await this.convertReblog(status.reblog, me) : null,
|
reblog: !isQuote ? this.convertReblog(status.reblog, me) : null,
|
||||||
content: content,
|
content: content,
|
||||||
content_type: 'text/x.misskeymarkdown',
|
content_type: 'text/x.misskeymarkdown',
|
||||||
text: note.text,
|
text: note.text,
|
||||||
created_at: status.created_at,
|
created_at: status.created_at,
|
||||||
|
edited_at: note.updatedAt?.toISOString() ?? null,
|
||||||
emojis: emoji,
|
emojis: emoji,
|
||||||
replies_count: note.repliesCount,
|
replies_count: note.repliesCount,
|
||||||
reblogs_count: note.renoteCount,
|
reblogs_count: note.renoteCount,
|
||||||
|
@ -301,7 +301,7 @@ export class MastoConverters {
|
||||||
reblogged,
|
reblogged,
|
||||||
favourited: status.favourited,
|
favourited: status.favourited,
|
||||||
muted: status.muted,
|
muted: status.muted,
|
||||||
sensitive: status.sensitive,
|
sensitive: status.sensitive || !!note.cw,
|
||||||
spoiler_text: note.cw ?? '',
|
spoiler_text: note.cw ?? '',
|
||||||
visibility: status.visibility,
|
visibility: status.visibility,
|
||||||
media_attachments: status.media_attachments.map(a => convertAttachment(a)),
|
media_attachments: status.media_attachments.map(a => convertAttachment(a)),
|
||||||
|
@ -312,15 +312,14 @@ export class MastoConverters {
|
||||||
application: null, //FIXME
|
application: null, //FIXME
|
||||||
language: null, //FIXME
|
language: null, //FIXME
|
||||||
pinned: false, //FIXME
|
pinned: false, //FIXME
|
||||||
reactions,
|
|
||||||
emoji_reactions: reactions,
|
|
||||||
bookmarked: false, //FIXME
|
bookmarked: false, //FIXME
|
||||||
quote: isQuote ? await this.convertReblog(status.reblog, me) : null,
|
quote_id: isQuote ? status.reblog?.id : undefined,
|
||||||
edited_at: note.updatedAt?.toISOString() ?? null,
|
quote: isQuote ? this.convertReblog(status.reblog, me) : null,
|
||||||
|
reactions: status.emoji_reactions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convertConversation(conversation: Entity.Conversation, me?: MiLocalUser | null): Promise<MastodonEntity.Conversation> {
|
public async convertConversation(conversation: Entity.Conversation, me: MiLocalUser | null): Promise<MastodonEntity.Conversation> {
|
||||||
return {
|
return {
|
||||||
id: conversation.id,
|
id: conversation.id,
|
||||||
accounts: await Promise.all(conversation.accounts.map(a => this.convertAccount(a))),
|
accounts: await Promise.all(conversation.accounts.map(a => this.convertAccount(a))),
|
||||||
|
@ -329,7 +328,7 @@ export class MastoConverters {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convertNotification(notification: Entity.Notification, me?: MiLocalUser | null): Promise<MastodonEntity.Notification> {
|
public async convertNotification(notification: Entity.Notification, me: MiLocalUser | null): Promise<MastodonEntity.Notification> {
|
||||||
return {
|
return {
|
||||||
account: await this.convertAccount(notification.account),
|
account: await this.convertAccount(notification.account),
|
||||||
created_at: notification.created_at,
|
created_at: notification.created_at,
|
||||||
|
@ -339,12 +338,23 @@ export class MastoConverters {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convertReaction(reaction: Entity.Reaction): Promise<Entity.Reaction> {
|
// public convertEmoji(emoji: string): MastodonEntity.Emoji {
|
||||||
if (reaction.accounts) {
|
// const reaction: MastodonEntity.Reaction = {
|
||||||
reaction.accounts = await Promise.all(reaction.accounts.map(a => this.convertAccount(a)));
|
// name: emoji,
|
||||||
}
|
// count: 1,
|
||||||
return reaction;
|
// };
|
||||||
}
|
//
|
||||||
|
// if (emoji.startsWith(':')) {
|
||||||
|
// const [, name] = emoji.match(/^:([^@:]+(?:@[^@:]+)?):$/) ?? [];
|
||||||
|
// if (name) {
|
||||||
|
// const url = `${this.config.url}/emoji/${name}.webp`;
|
||||||
|
// reaction.url = url;
|
||||||
|
// reaction.static_url = url;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return reaction;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
function simpleConvert<T>(data: T): T {
|
function simpleConvert<T>(data: T): T {
|
||||||
|
@ -423,7 +433,3 @@ export function convertRelationship(relationship: Partial<Entity.Relationship> &
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
export function convertStatusSource(status: Entity.StatusSource): MastodonEntity.StatusSource {
|
|
||||||
return simpleConvert(status);
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,13 +29,17 @@ export class ApiNotificationsMastodon {
|
||||||
fastify.get<ApiNotifyMastodonRoute>('/v1/notifications', async (request, reply) => {
|
fastify.get<ApiNotifyMastodonRoute>('/v1/notifications', async (request, reply) => {
|
||||||
const { client, me } = await this.clientService.getAuthClient(request);
|
const { client, me } = await this.clientService.getAuthClient(request);
|
||||||
const data = await client.getNotifications(parseTimelineArgs(request.query));
|
const data = await client.getNotifications(parseTimelineArgs(request.query));
|
||||||
const response = await Promise.all(data.data.map(async n => {
|
const notifications = await Promise.all(data.data.map(n => this.mastoConverters.convertNotification(n, me)));
|
||||||
const converted = await this.mastoConverters.convertNotification(n, me);
|
const response: MastodonEntity.Notification[] = [];
|
||||||
if (converted.type === 'reaction') {
|
for (const notification of notifications) {
|
||||||
converted.type = 'favourite';
|
response.push(notification);
|
||||||
|
if (notification.type === 'reaction') {
|
||||||
|
response.push({
|
||||||
|
...notification,
|
||||||
|
type: 'favourite',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return converted;
|
}
|
||||||
}));
|
|
||||||
|
|
||||||
attachMinMaxPagination(request, reply, response);
|
attachMinMaxPagination(request, reply, response);
|
||||||
reply.send(response);
|
reply.send(response);
|
||||||
|
@ -46,12 +50,9 @@ export class ApiNotificationsMastodon {
|
||||||
|
|
||||||
const { client, me } = await this.clientService.getAuthClient(_request);
|
const { client, me } = await this.clientService.getAuthClient(_request);
|
||||||
const data = await client.getNotification(_request.params.id);
|
const data = await client.getNotification(_request.params.id);
|
||||||
const converted = await this.mastoConverters.convertNotification(data.data, me);
|
const response = await this.mastoConverters.convertNotification(data.data, me);
|
||||||
if (converted.type === 'reaction') {
|
|
||||||
converted.type = 'favourite';
|
|
||||||
}
|
|
||||||
|
|
||||||
reply.send(converted);
|
reply.send(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.post<ApiNotifyMastodonRoute & { Params: { id?: string } }>('/v1/notification/:id/dismiss', { preHandler: upload.single('none') }, async (_request, reply) => {
|
fastify.post<ApiNotifyMastodonRoute & { Params: { id?: string } }>('/v1/notification/:id/dismiss', { preHandler: upload.single('none') }, async (_request, reply) => {
|
||||||
|
|
|
@ -6,5 +6,7 @@ namespace Entity {
|
||||||
me: boolean
|
me: boolean
|
||||||
name: string
|
name: string
|
||||||
accounts?: Array<Account>
|
accounts?: Array<Account>
|
||||||
|
url?: string
|
||||||
|
static_url?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
packages/megalodon/src/mastodon/entities/reaction.ts
Normal file
16
packages/megalodon/src/mastodon/entities/reaction.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference path="account.ts" />
|
||||||
|
|
||||||
|
namespace MastodonEntity {
|
||||||
|
export type Reaction = {
|
||||||
|
name: string
|
||||||
|
count: number
|
||||||
|
me?: boolean
|
||||||
|
url?: string
|
||||||
|
static_url?: string
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
/// <reference path="emoji.ts" />
|
/// <reference path="emoji.ts" />
|
||||||
/// <reference path="card.ts" />
|
/// <reference path="card.ts" />
|
||||||
/// <reference path="poll.ts" />
|
/// <reference path="poll.ts" />
|
||||||
|
/// <reference path="reaction.ts" />
|
||||||
|
|
||||||
namespace MastodonEntity {
|
namespace MastodonEntity {
|
||||||
export type Status = {
|
export type Status = {
|
||||||
|
@ -41,6 +42,8 @@ namespace MastodonEntity {
|
||||||
// These parameters are unique parameters in fedibird.com for quote.
|
// These parameters are unique parameters in fedibird.com for quote.
|
||||||
quote_id?: string
|
quote_id?: string
|
||||||
quote?: Status | null
|
quote?: Status | null
|
||||||
|
// These parameters are unique to glitch-soc for emoji reactions.
|
||||||
|
reactions?: Reaction[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StatusTag = {
|
export type StatusTag = {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
/// <reference path="./entities/poll_option.ts" />
|
/// <reference path="./entities/poll_option.ts" />
|
||||||
/// <reference path="./entities/preferences.ts" />
|
/// <reference path="./entities/preferences.ts" />
|
||||||
/// <reference path="./entities/push_subscription.ts" />
|
/// <reference path="./entities/push_subscription.ts" />
|
||||||
|
/// <reference path="./entities/reaction.ts" />
|
||||||
/// <reference path="./entities/relationship.ts" />
|
/// <reference path="./entities/relationship.ts" />
|
||||||
/// <reference path="./entities/report.ts" />
|
/// <reference path="./entities/report.ts" />
|
||||||
/// <reference path="./entities/results.ts" />
|
/// <reference path="./entities/results.ts" />
|
||||||
|
|
|
@ -2555,6 +2555,7 @@ export default class Misskey implements MegalodonInterface {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO implement
|
||||||
public async getEmojiReaction(_id: string, _emoji: string): Promise<Response<Entity.Reaction>> {
|
public async getEmojiReaction(_id: string, _emoji: string): Promise<Response<Entity.Reaction>> {
|
||||||
return new Promise((_, reject) => {
|
return new Promise((_, reject) => {
|
||||||
const err = new NoImplementedError('misskey does not support')
|
const err = new NoImplementedError('misskey does not support')
|
||||||
|
|
|
@ -286,6 +286,7 @@ namespace MisskeyAPI {
|
||||||
plain_content: n.text ? n.text : null,
|
plain_content: n.text ? n.text : null,
|
||||||
created_at: n.createdAt,
|
created_at: n.createdAt,
|
||||||
edited_at: n.updatedAt || null,
|
edited_at: n.updatedAt || null,
|
||||||
|
// TODO this is probably wrong
|
||||||
emojis: mapEmojis(n.emojis).concat(mapReactionEmojis(n.reactionEmojis)),
|
emojis: mapEmojis(n.emojis).concat(mapReactionEmojis(n.reactionEmojis)),
|
||||||
replies_count: n.repliesCount,
|
replies_count: n.repliesCount,
|
||||||
reblogs_count: n.renoteCount,
|
reblogs_count: n.renoteCount,
|
||||||
|
@ -304,7 +305,7 @@ namespace MisskeyAPI {
|
||||||
application: null,
|
application: null,
|
||||||
language: null,
|
language: null,
|
||||||
pinned: null,
|
pinned: null,
|
||||||
emoji_reactions: typeof n.reactions === 'object' ? mapReactions(n.reactions, n.myReaction) : [],
|
emoji_reactions: typeof n.reactions === 'object' ? mapReactions(n.reactions, n.reactionEmojis, n.myReaction) : [],
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
quote: n.renote && n.text ? note(n.renote, n.user.host ? n.user.host : host ? host : null) : null
|
quote: n.renote && n.text ? note(n.renote, n.user.host ? n.user.host : host ? host : null) : null
|
||||||
}
|
}
|
||||||
|
@ -334,23 +335,20 @@ namespace MisskeyAPI {
|
||||||
) : 0;
|
) : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mapReactions = (r: { [key: string]: number }, myReaction?: string): Array<MegalodonEntity.Reaction> => {
|
export const mapReactions = (r: { [key: string]: number }, e: Record<string, string | undefined>, myReaction?: string): Array<MegalodonEntity.Reaction> => {
|
||||||
return Object.keys(r).map(key => {
|
return Object.keys(r).map(key => {
|
||||||
if (myReaction && key === myReaction) {
|
const me = myReaction != null && key === myReaction;
|
||||||
return {
|
return {
|
||||||
count: r[key],
|
count: r[key],
|
||||||
me: true,
|
me,
|
||||||
name: key
|
name: key,
|
||||||
}
|
url: e[key],
|
||||||
}
|
static_url: e[key],
|
||||||
return {
|
}
|
||||||
count: r[key],
|
|
||||||
me: false,
|
|
||||||
name: key
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO implement other properties
|
||||||
const mapReactionEmojis = (r: { [key: string]: string }): Array<MegalodonEntity.Emoji> => {
|
const mapReactionEmojis = (r: { [key: string]: string }): Array<MegalodonEntity.Emoji> => {
|
||||||
return Object.keys(r).map(key => ({
|
return Object.keys(r).map(key => ({
|
||||||
shortcode: key,
|
shortcode: key,
|
||||||
|
@ -371,7 +369,7 @@ namespace MisskeyAPI {
|
||||||
result.push({
|
result.push({
|
||||||
count: 1,
|
count: 1,
|
||||||
me: false,
|
me: false,
|
||||||
name: e.type
|
name: e.type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue