Implement getting and dismissing server announcements (#4)

to be fair, I didn't test it on a real mastodon server. I just hope it works. I also don't think it matters much anyway as this is a fork for calckey? idk please let me know if I need to test it on mastodon too.

It's a bit silly implementation as mfm is just escaped to plaintext and the announcement title is glued to `content`, but overall I think it works fine :)

Reviewed-on: https://codeberg.org/calckey/megalodon/pulls/4
Co-authored-by: fruye <fruye@unix.dog>
Co-committed-by: fruye <fruye@unix.dog>
This commit is contained in:
fruye 2023-04-23 22:31:56 +00:00 committed by Laura Hausmann
parent ee8d042bb8
commit 3e9e1635e2
Signed by: zotan
GPG key ID: D044E84C5BE01605
15 changed files with 242 additions and 0 deletions

View file

@ -0,0 +1,34 @@
/// <reference path="tag.ts" />
/// <reference path="emoji.ts" />
/// <reference path="reaction.ts" />
namespace Entity {
export type Announcement = {
id: string
content: string
starts_at: string | null
ends_at: string | null
published: boolean
all_day: boolean
published_at: string
updated_at: string
read?: boolean
mentions: Array<AnnouncementAccount>
statuses: Array<AnnouncementStatus>
tags: Array<Tag>
emojis: Array<Emoji>
reactions: Array<Reaction>
}
export type AnnouncementAccount = {
id: string
username: string
url: string
acct: string
}
export type AnnouncementStatus = {
id: string
url: string
}
}

View file

@ -1,5 +1,6 @@
/// <reference path="./entities/account.ts" />
/// <reference path="./entities/activity.ts" />
/// <reference path="./entities/announcement.ts" />
/// <reference path="./entities/application.ts" />
/// <reference path="./entities/async_attachment.ts" />
/// <reference path="./entities/attachment.ts" />

View file

@ -2221,6 +2221,26 @@ export default class Mastodon implements MegalodonInterface {
})
}
// ======================================
// instance/announcements
// ======================================
public async getInstanceAnnouncements(with_dismissed?: boolean | null): Promise<Response<Array<Entity.Announcement>>> {
let params = {}
if (with_dismissed) {
params = Object.assign(params, {
with_dismissed
})
}
return this.client.get<Array<MastodonAPI.Entity.Announcement>>('/api/v1/announcements', params).then(res => ({
...res,
data: res.data.map(t => MastodonAPI.Converter.announcement(t))
}))
}
public async dismissInstanceAnnouncement(id: string): Promise<Response<{}>> {
return this.client.post<{}>(`/api/v1/announcements/${id}/dismiss`)
}
// ======================================
// Emoji reactions
// ======================================

View file

@ -444,6 +444,7 @@ namespace MastodonAPI {
export namespace Entity {
export type Account = MastodonEntity.Account
export type Activity = MastodonEntity.Activity
export type Announcement = MastodonEntity.Announcement
export type Application = MastodonEntity.Application
export type AsyncAttachment = MegalodonEntity.AsyncAttachment
export type Attachment = MastodonEntity.Attachment
@ -466,6 +467,7 @@ namespace MastodonAPI {
export type Preferences = MastodonEntity.Preferences
export type PushSubscription = MastodonEntity.PushSubscription
export type Relationship = MastodonEntity.Relationship
export type Reaction = MastodonEntity.Reaction
export type Report = MastodonEntity.Report
export type Results = MastodonEntity.Results
export type ScheduledStatus = MastodonEntity.ScheduledStatus
@ -523,6 +525,10 @@ namespace MastodonAPI {
export const account = (a: Entity.Account): MegalodonEntity.Account => a
export const activity = (a: Entity.Activity): MegalodonEntity.Activity => a
export const announcement = (a: Entity.Announcement): MegalodonEntity.Announcement => ({
...a,
reactions: a.reactions.map(r => reaction(r))
})
export const application = (a: Entity.Application): MegalodonEntity.Application => a
export const attachment = (a: Entity.Attachment): MegalodonEntity.Attachment => a
export const async_attachment = (a: Entity.AsyncAttachment) => {
@ -586,6 +592,12 @@ namespace MastodonAPI {
export const preferences = (p: Entity.Preferences): MegalodonEntity.Preferences => p
export const push_subscription = (p: Entity.PushSubscription): MegalodonEntity.PushSubscription => p
export const relationship = (r: Entity.Relationship): MegalodonEntity.Relationship => r
export const reaction = (r: Entity.Reaction): MegalodonEntity.Reaction => ({
count: r.count,
me: r.me ?? false,
name: r.name,
url: r.url,
})
export const report = (r: Entity.Report): MegalodonEntity.Report => r
export const results = (r: Entity.Results): MegalodonEntity.Results => ({
accounts: r.accounts.map(a => account(a)),

View file

@ -0,0 +1,41 @@
/// <reference path="tag.ts" />
/// <reference path="emoji.ts" />
namespace MastodonEntity {
export type Announcement = {
id: string
content: string
starts_at: string | null
ends_at: string | null
published: boolean
all_day: boolean
published_at: string
updated_at: string
read?: boolean
mentions: Array<AnnouncementAccount>
statuses: Array<AnnouncementStatus>
tags: Array<Tag>
emojis: Array<Emoji>
reactions: Array<Reaction>
}
export type AnnouncementAccount = {
id: string
username: string
url: string
acct: string
}
export type AnnouncementStatus = {
id: string
url: string
}
export type Reaction = {
name: string
count: number
me?: boolean
url?: string
static_url?: string
}
}

View file

@ -1,5 +1,6 @@
/// <reference path="./entities/account.ts" />
/// <reference path="./entities/activity.ts" />
/// <reference path="./entities/announcement.ts" />
/// <reference path="./entities/application.ts" />
/// <reference path="./entities/async_attachment.ts" />
/// <reference path="./entities/attachment.ts" />

View file

@ -1256,6 +1256,22 @@ export interface MegalodonInterface {
*/
getInstanceCustomEmojis(): Promise<Response<Array<Entity.Emoji>>>
// ======================================
// instance/announcements
// ======================================
/**
* GET /api/v1/announcements
*
* @param with_dismissed Include announcements dismissed by the user. Defaults to false.
* @return Array of announcements.
*/
getInstanceAnnouncements(with_dismissed?: boolean | null): Promise<Response<Array<Entity.Announcement>>>
/**
* POST /api/v1/announcements/:id/dismiss
*/
dismissInstanceAnnouncement(id: string): Promise<Response<{}>>
// ======================================
// Emoji reactions
// ======================================

View file

@ -2107,6 +2107,26 @@ export default class Misskey implements MegalodonInterface {
.then(res => ({ ...res, data: res.data.emojis.map(e => MisskeyAPI.Converter.emoji(e)) }))
}
// ======================================
// instance/announcements
// ======================================
public async getInstanceAnnouncements(with_dismissed?: boolean | null): Promise<Response<Array<Entity.Announcement>>> {
let params = {}
if (with_dismissed) {
params = Object.assign(params, {
withUnreads: with_dismissed
})
}
return this.client.post<Array<MisskeyAPI.Entity.Announcement>>('/api/announcements', params).then(res => ({
...res,
data: res.data.map(t => MisskeyAPI.Converter.announcement(t))
}))
}
public async dismissInstanceAnnouncement(id: string): Promise<Response<{}>> {
return this.client.post<{}>('/api/i/read-announcement', { announcementId: id })
}
// ======================================
// Emoji reactions
// ======================================

View file

@ -14,6 +14,7 @@ import NotificationType from '../notification'
namespace MisskeyAPI {
export namespace Entity {
export type App = MisskeyEntity.App
export type Announcement = MisskeyEntity.Announcement
export type Blocking = MisskeyEntity.Blocking
export type Choice = MisskeyEntity.Choice
export type CreatedNote = MisskeyEntity.CreatedNote
@ -384,6 +385,34 @@ namespace MisskeyAPI {
moved: null
}
export const announcement = (a: Entity.Announcement): MegalodonEntity.Announcement => {
// Reused from Note converter.
const escapeHTML = (text: string) => text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/`/g, '&#x60;')
.replace(/\r?\n/g, '<br>');
return {
id: a.id,
content: `<h1>${escapeHTML(a.title)}</h1>${escapeHTML(a.text)}`,
starts_at: null,
ends_at: null,
published: true,
all_day: false,
published_at: a.createdAt,
updated_at: a.updatedAt,
read: a.isRead,
mentions: [],
statuses: [],
tags: [],
emojis: [],
reactions: [],
};
}
export const notification = (n: Entity.Notification, host: string): MegalodonEntity.Notification => {
let notification = {
id: n.id,

View file

@ -0,0 +1,10 @@
namespace MisskeyEntity {
export type Announcement = {
id: string
createdAt: string
updatedAt: string
text: string
title: string
isRead?: boolean
}
}

View file

@ -1,4 +1,5 @@
/// <reference path="entities/app.ts" />
/// <reference path="entities/announcement.ts" />
/// <reference path="entities/blocking.ts" />
/// <reference path="entities/createdNote.ts" />
/// <reference path="entities/emoji.ts" />

View file

@ -2229,6 +2229,26 @@ export default class Pleroma implements MegalodonInterface {
})
}
// ======================================
// instance/announcements
// ======================================
public async getInstanceAnnouncements(with_dismissed?: boolean | null): Promise<Response<Array<Entity.Announcement>>> {
let params = {}
if (with_dismissed) {
params = Object.assign(params, {
with_dismissed
})
}
return this.client.get<Array<PleromaAPI.Entity.Announcement>>('/api/v1/announcements', params).then(res => ({
...res,
data: res.data.map(t => PleromaAPI.Converter.announcement(t))
}))
}
public async dismissInstanceAnnouncement(id: string): Promise<Response<{}>> {
return this.client.post<{}>(`/api/v1/announcements/${id}/dismiss`)
}
// ======================================
// Emoji reactions
// ======================================

View file

@ -15,6 +15,7 @@ namespace PleromaAPI {
export namespace Entity {
export type Account = PleromaEntity.Account
export type Activity = PleromaEntity.Activity
export type Announcement = PleromaEntity.Announcement
export type Application = PleromaEntity.Application
export type AsyncAttachment = PleromaEntity.AsyncAttachment
export type Attachment = PleromaEntity.Attachment
@ -94,6 +95,7 @@ namespace PleromaAPI {
export const account = (a: Entity.Account): MegalodonEntity.Account => a
export const activity = (a: Entity.Activity): MegalodonEntity.Activity => a
export const announcement = (a: Entity.Announcement): MegalodonEntity.Announcement => a
export const application = (a: Entity.Application): MegalodonEntity.Application => a
export const attachment = (a: Entity.Attachment): MegalodonEntity.Attachment => a
export const async_attachment = (a: Entity.AsyncAttachment) => {

View file

@ -0,0 +1,34 @@
/// <reference path="tag.ts" />
/// <reference path="emoji.ts" />
/// <reference path="reaction.ts" />
namespace PleromaEntity {
export type Announcement = {
id: string
content: string
starts_at: string | null
ends_at: string | null
published: boolean
all_day: boolean
published_at: string
updated_at: string
read?: boolean
mentions: Array<AnnouncementAccount>
statuses: Array<AnnouncementStatus>
tags: Array<Tag>
emojis: Array<Emoji>
reactions: Array<Reaction>
}
export type AnnouncementAccount = {
id: string
username: string
url: string
acct: string
}
export type AnnouncementStatus = {
id: string
url: string
}
}

View file

@ -1,5 +1,6 @@
/// <reference path="./entities/account.ts" />
/// <reference path="./entities/activity.ts" />
/// <reference path="./entities/announcement.ts" />
/// <reference path="./entities/application.ts" />
/// <reference path="./entities/async_attachment.ts" />
/// <reference path="./entities/attachment.ts" />