Skip to content
This repository has been archived by the owner on Nov 13, 2024. It is now read-only.

Commit

Permalink
add latest api changes, fix #4
Browse files Browse the repository at this point in the history
  • Loading branch information
williamhorning committed Aug 5, 2024
1 parent a386e5c commit dae507d
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 38 deletions.
14 changes: 14 additions & 0 deletions src/api/uploads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ export interface uploads_opts {
export enum upload_types {
attachment = 'attachments',
icon = 'icons',
sticker = 'stickers',
emoji = 'emojis',
}

/** check if object is an api reaction */
export function is_api_attachment(obj: unknown): obj is api_attachment {
if (obj === null || typeof obj !== 'object') return false;
if (!('count' in obj) || typeof obj.count !== 'number') return false;
if (!('emoji' in obj) || typeof obj.emoji !== 'string') return false;
if (!('user_reacted' in obj) || typeof obj.user_reacted !== 'boolean') {
return false;
}

return true;
}

/** access to meower uploads */
Expand Down
29 changes: 25 additions & 4 deletions src/interfaces/chat.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { is_api_attachment, type api_attachment } from '../api/uploads.ts';
import { type api_post, post } from './post.ts';

/** chat types */
Expand Down Expand Up @@ -32,6 +33,10 @@ export interface api_chat {
owner: string;
/** chat type */
type: chat_type;
/** emojis */
emojis: api_attachment[];
/** stickers */
stickers: api_attachment[];
}

/** chat construction options */
Expand Down Expand Up @@ -64,6 +69,10 @@ export interface message_send_opts {
content: string;
/** message attachments */
attachments?: string[];
/** reply id list */
reply_to?: string[];
/** stickers */
stickers?: string[];
}

/** check if a value is a chat */
Expand All @@ -86,6 +95,14 @@ export function is_api_chat(obj: unknown): obj is api_chat {
if (!('nickname' in obj) || typeof obj.nickname !== 'string') return false;
if (!('owner' in obj) || typeof obj.owner !== 'string') return false;
if (!('type' in obj) || typeof obj.type !== 'number') return false;
if (!('emojis' in obj) || !Array.isArray(obj.emojis)) return false;
for (const i of obj.emojis) {
if (!is_api_attachment(i)) return false;
}
if (!('stickers' in obj) || !Array.isArray(obj.stickers)) return false;
for (const i of obj.stickers) {
if (!is_api_attachment(i)) return false;
}

return true;
}
Expand Down Expand Up @@ -119,6 +136,10 @@ export class chat {
owner!: string;
/** chat type */
type!: chat_type;
/** emojis */
emojis!: api_attachment[];
/** stickers */
stickers!: api_attachment[];

constructor(opts: chat_construction_opts) {
this.api_url = opts.api_url;
Expand All @@ -143,6 +164,8 @@ export class chat {
this.nickname = this.raw.nickname;
this.owner = this.raw.owner;
this.type = this.raw.type;
this.emojis = this.raw.emojis;
this.stickers = this.raw.stickers;
}

/** leave the chat */
Expand Down Expand Up @@ -273,7 +296,7 @@ export class chat {
}

/** send a message */
async send_message(content: string | message_send_opts): Promise<post> {
async send_message(content: message_send_opts): Promise<post> {
let url = `${this.api_url}/posts/${this.id}`;
if (this.id === 'home') url = `${this.api_url}/home`;

Expand All @@ -283,9 +306,7 @@ export class chat {
token: this.api_token,
'Content-Type': 'application/json',
},
body: JSON.stringify(
typeof content === 'string' ? { content } : content,
),
body: JSON.stringify(content),
});

const data = await resp.json();
Expand Down
145 changes: 111 additions & 34 deletions src/interfaces/post.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { api_attachment } from '../api/uploads.ts';
import { type api_attachment, is_api_attachment } from '../api/uploads.ts';
import type { message_send_opts } from './chat.ts';

/** types of posts */
Expand All @@ -9,17 +9,12 @@ export enum post_type {
inbox = 2,
}

/** bridge users */
export const bridge_users = ['Discord', 'boltcanary', 'bolt'];

/** raw post data */
export interface api_post {
/** attachments */
attachments?: api_attachment[];
/** is the post pinned */
pinned: boolean;
/** bridged post */
bridged?: api_post;
/** post id */
_id: string;
/** is the post deleted */
Expand All @@ -39,6 +34,16 @@ export interface api_post {
type: post_type;
/** username */
u: string;
/** stickers */
stickers: api_attachment[];
/** reply to */
reply_to: api_post[];
/** reactions */
reactions: {
count: number;
emoji: string;
user_reacted: boolean;
}[]
}

/** post creation options */
Expand Down Expand Up @@ -92,6 +97,18 @@ export function is_api_post(obj: unknown): obj is api_post {
if (!('e' in obj.t) || typeof obj.t.e !== 'number') return false;
if (!('type' in obj) || typeof obj.type !== 'number') return false;
if (!('u' in obj) || typeof obj.u !== 'string') return false;
if (!('stickers' in obj) || !Array.isArray(obj.stickers)) return false;
for (const i of obj.stickers) {
if (!is_api_attachment(i)) return false;
}
if (!('reply_to' in obj) || !Array.isArray(obj.reply_to)) return false;
for (const i of obj.reply_to) {
if (!is_api_post(i)) return false;
}
if (!('reactions' in obj) || !Array.isArray(obj.reactions)) return false;
for (const i of obj.reactions) {
if (!is_api_attachment(i)) return false;
}

return true;
}
Expand All @@ -104,13 +121,11 @@ export class post {
/** raw api data */
raw: api_post;
/** attachments */
attachments?: api_attachment[];
attachments!: api_attachment[];
/** post id */
id!: string;
/** whether the post in pinned */
pinned!: boolean;
/** bridged post, if any */
bridged?: post;
/** is the post deleted */
deleted!: boolean;
/** post content */
Expand All @@ -123,6 +138,12 @@ export class post {
type!: post_type;
/** username */
username!: string;
/** reply to */
replies!: post[]
/** stickers */
stickers!: api_attachment[];
/** reactions */
reactions!: api_post['reactions'];

constructor(opts: post_construction_opts) {
this.api_url = opts.api_url;
Expand All @@ -136,30 +157,21 @@ export class post {
}

private assign_data() {
this.attachments = this.raw.attachments;
this.attachments = this.raw.attachments || [];
this.id = this.raw._id;
this.pinned = this.raw.pinned;
this.bridged = this.raw.bridged
? new post({
api_token: this.api_token,
api_url: this.api_url,
api_username: this.api_username,
data: this.raw.bridged,
})
: undefined;
this.deleted = this.raw.isDeleted;
this.chat_id = this.raw.post_origin;
this.timestamp = this.raw.t.e;
this.type = this.raw.type;

if (!bridge_users.includes(this.raw.u)) {
this.content = this.raw.p;
this.username = this.raw.u;
} else {
const [username, content] = this.raw.p.split(': ');
this.content = content;
this.username = username;
}
this.replies = this.raw.reply_to.map(i=>new post({
api_token: this.api_token,
api_url: this.api_url,
api_username: this.api_username,
data: i,
}));
this.stickers = this.raw.stickers;
this.reactions = this.raw.reactions;
}

/** delete the post */
Expand Down Expand Up @@ -269,11 +281,7 @@ export class post {
}

/** reply to the post */
async reply(content: string | message_send_opts): Promise<post> {
let text_content = typeof content === 'string' ? content : content.content;

text_content = `@${this.api_username} "" (${this.id})\n${content}`;

async reply(content: message_send_opts): Promise<post> {
let url = `${this.api_url}/posts/${this.chat_id}`;

if (this.chat_id === 'home') url = `${this.api_url}/home`;
Expand All @@ -285,8 +293,8 @@ export class post {
'token': this.api_token,
},
body: JSON.stringify({
...(typeof content !== 'string' ? content : {}),
content: text_content,
...content,
reply_to: [...content.reply_to ?? [], this.id],
}),
})).json();

Expand All @@ -301,4 +309,73 @@ export class post {
data: resp,
});
}

/** react to the post */
async react(emoji: string) {
const url = `${this.api_url}/posts/${this.id}/reactions`;

const resp = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'token': this.api_token,
},
body: JSON.stringify({
emoji,
}),
});

if (!resp.ok) {
throw new Error('failed to react to post', {
cause: await resp.json(),
});
}

const found_emoji_index = this.reactions.findIndex(i=>i.emoji === emoji);
const found_raw_emoji_index = this.raw.reactions.findIndex(i=>i.emoji === emoji);

if (found_emoji_index === -1) {
const new_reaction = {
emoji,
count: 1,
user_reacted: true,
}

this.reactions.push(new_reaction);
this.raw.reactions.push(new_reaction);
} else {
this.reactions[found_emoji_index].count++;
this.raw.reactions[found_raw_emoji_index].count++;
}
}

/** remove reaction */
async remove_reaction(emoji: string) {
const url = `${this.api_url}/posts/${this.id}/reactions`;

const resp = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'token': this.api_token,
},
body: JSON.stringify({
emoji,
}),
});

if (!resp.ok) {
throw new Error('failed to remove reaction from post', {
cause: await resp.json(),
});
}

const found_emoji_index = this.reactions.findIndex(i=>i.emoji === emoji);
const found_raw_emoji_index = this.raw.reactions.findIndex(i=>i.emoji === emoji);

if (found_emoji_index !== -1) {
this.reactions[found_emoji_index].count--;
this.raw.reactions[found_raw_emoji_index].count--;
}
}
}

0 comments on commit dae507d

Please sign in to comment.