Sengi-Windows-MacOS-Linux/src/app/services/mastodon.service.ts

411 lines
21 KiB
TypeScript
Raw Normal View History

import { Injectable } from '@angular/core';
2019-03-23 22:43:59 +01:00
import { HttpHeaders, HttpClient, HttpResponse } from '@angular/common/http';
import { ApiRoutes } from './models/api.settings';
2019-08-03 02:56:52 +02:00
import { Account, Status, Results, Context, Relationship, Instance, Attachment, Notification, List, Poll, Emoji, Conversation } from "./models/mastodon.interfaces";
import { AccountInfo } from '../states/accounts.state';
2019-05-19 02:44:36 +02:00
import { StreamTypeEnum, StreamElement } from '../states/streams.state';
@Injectable()
2019-07-30 03:05:37 +02:00
export class MastodonService {
2018-09-16 08:09:48 +02:00
private apiRoutes = new ApiRoutes();
constructor(private readonly httpClient: HttpClient) { }
2019-03-10 19:36:22 +01:00
getInstance(instance: string): Promise<Instance> {
const route = `https://${instance}${this.apiRoutes.getInstance}`;
return this.httpClient.get<Instance>(route).toPromise();
}
2018-09-16 08:09:48 +02:00
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Account>('https://' + account.instance + this.apiRoutes.getCurrentAccount, { headers: headers }).toPromise();
}
2019-05-19 02:44:36 +02:00
getTimeline(account: AccountInfo, type: StreamTypeEnum, max_id: string = null, since_id: string = null, limit: number = 20, tag: string = null, listId: string = null): Promise<Status[]> {
const route = `https://${account.instance}${this.getTimelineRoute(type, max_id, since_id, limit, tag, listId)}`;
2018-09-16 08:09:48 +02:00
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Status[]>(route, { headers: headers }).toPromise();
2018-09-16 08:09:48 +02:00
}
2019-08-03 02:56:52 +02:00
getConversations(account: AccountInfo, max_id: string = null, since_id: string = null, min_id = null, limit: number = 20,): Promise<Conversation[]> {
let params = `?limit=${limit}`;
if (max_id) params += `&max_id=${max_id}`;
if (since_id) params += `&since_id=${since_id}`;
if (min_id) params += `&since_id=${min_id}`;
const route = `https://${account.instance}${this.apiRoutes.getConversations}${params}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Conversation[]>(route, { headers: headers }).toPromise();
}
2019-05-19 02:44:36 +02:00
private getTimelineRoute(type: StreamTypeEnum, max_id: string, since_id: string, limit: number, tag: string, listId: string): string {
2018-09-16 08:09:48 +02:00
let route: string;
switch (type) {
case StreamTypeEnum.personnal:
route = this.apiRoutes.getHomeTimeline;
break;
case StreamTypeEnum.local:
route = this.apiRoutes.getPublicTimeline + `?local=true&`;
break;
case StreamTypeEnum.global:
route = this.apiRoutes.getPublicTimeline + `?local=false&`;
break;
case StreamTypeEnum.directmessages:
route = this.apiRoutes.getDirectTimeline;
break;
case StreamTypeEnum.tag:
2018-11-03 06:38:52 +01:00
route = this.apiRoutes.getTagTimeline.replace('{0}', tag);
2018-09-16 08:09:48 +02:00
break;
case StreamTypeEnum.list:
2019-05-19 02:44:36 +02:00
route = this.apiRoutes.getListTimeline.replace('{0}', listId);
2018-09-16 08:09:48 +02:00
break;
default:
throw new Error('StreamTypeEnum not supported');
}
if (!route.includes('?')) route = route + '?';
if (max_id) route = route + `max_id=${max_id}&`;
if (since_id) route = route + `since_id=${since_id}&`;
if (limit) route = route + `limit=${limit}&`;
return this.trimChar(this.trimChar(route, '?'), '&');
}
2018-09-16 19:11:22 +02:00
private escapeRegExp(strToEscape) {
2018-09-16 08:09:48 +02:00
// Escape special characters for use in a regular expression
return strToEscape.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
};
2018-09-26 05:59:44 +02:00
2018-09-16 19:11:22 +02:00
private trimChar(origString, charToTrim) {
2018-09-16 08:09:48 +02:00
charToTrim = this.escapeRegExp(charToTrim);
var regEx = new RegExp("^[" + charToTrim + "]+|[" + charToTrim + "]+$", "g");
return origString.replace(regEx, "");
};
2019-03-10 22:38:10 +01:00
postNewStatus(account: AccountInfo, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, mediaIds: string[]): Promise<Status> {
const url = `https://${account.instance}${this.apiRoutes.postNewStatus}`;
2019-03-10 22:38:10 +01:00
const statusData = new StatusData();
statusData.status = status;
statusData.media_ids = mediaIds;
2018-09-26 05:59:44 +02:00
if (in_reply_to_id) {
2019-03-10 22:38:10 +01:00
statusData.in_reply_to_id = in_reply_to_id;
2018-09-26 05:59:44 +02:00
}
if (spoiler) {
2019-03-10 22:38:10 +01:00
statusData.sensitive = true;
statusData.spoiler_text = spoiler;
2018-09-26 05:59:44 +02:00
}
switch (visibility) {
case VisibilityEnum.Public:
2019-03-10 22:38:10 +01:00
statusData.visibility = 'public';
2018-09-26 05:59:44 +02:00
break;
case VisibilityEnum.Unlisted:
2019-03-10 22:38:10 +01:00
statusData.visibility = 'unlisted';
2018-09-26 05:59:44 +02:00
break;
case VisibilityEnum.Private:
2019-03-10 22:38:10 +01:00
statusData.visibility = 'private';
2018-09-26 05:59:44 +02:00
break;
case VisibilityEnum.Direct:
2019-03-10 22:38:10 +01:00
statusData.visibility = 'direct';
2018-09-26 05:59:44 +02:00
break;
default:
2019-03-10 22:38:10 +01:00
statusData.visibility = 'private';
2018-09-26 05:59:44 +02:00
break;
}
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
2019-03-10 22:38:10 +01:00
return this.httpClient.post<Status>(url, statusData, { headers: headers }).toPromise();
}
2019-07-08 00:58:56 +02:00
getStatus(account: AccountInfo, statusId: string): Promise<Status> {
const route = `https://${account.instance}${this.apiRoutes.getStatus.replace('{0}', statusId)}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Status>(route, { headers: headers }).toPromise()
}
2019-03-10 19:36:22 +01:00
search(account: AccountInfo, query: string, resolve: boolean = false): Promise<Results> {
if (query[0] === '#') query = query.substr(1);
const route = `https://${account.instance}${this.apiRoutes.search}?q=${query}&resolve=${resolve}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Results>(route, { headers: headers }).toPromise()
}
2018-10-04 02:13:31 +02:00
2019-03-10 19:36:22 +01:00
getAccountStatuses(account: AccountInfo, targetAccountId: number, onlyMedia: boolean, onlyPinned: boolean, excludeReplies: boolean, maxId: string, sinceId: string, limit: number = 20): Promise<Status[]> {
2018-11-01 05:44:58 +01:00
const route = `https://${account.instance}${this.apiRoutes.getAccountStatuses}`.replace('{0}', targetAccountId.toString());
let params = `?only_media=${onlyMedia}&pinned=${onlyPinned}&exclude_replies=${excludeReplies}&limit=${limit}`;
2019-03-10 19:36:22 +01:00
if (maxId) params += `&max_id=${maxId}`;
if (sinceId) params += `&since_id=${sinceId}`;
2018-11-01 05:44:58 +01:00
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
2019-03-10 19:36:22 +01:00
return this.httpClient.get<Status[]>(route + params, { headers: headers }).toPromise();
2018-11-01 05:44:58 +01:00
}
2019-03-10 19:36:22 +01:00
getStatusContext(account: AccountInfo, targetStatusId: string): Promise<Context> {
2019-01-28 05:08:43 +01:00
const params = this.apiRoutes.getStatusContext.replace('{0}', targetStatusId);
const route = `https://${account.instance}${params}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Context>(route, { headers: headers }).toPromise();
}
2019-03-23 22:43:59 +01:00
getFavorites(account: AccountInfo, maxId: string = null): Promise<FavoriteResult> { //, minId: string = null
let route = `https://${account.instance}${this.apiRoutes.getFavourites}`; //?limit=${limit}
if (maxId) route += `?max_id=${maxId}`;
//if (minId) route += `&min_id=${minId}`;
2019-03-23 21:34:46 +01:00
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
2019-03-23 22:43:59 +01:00
return this.httpClient.get(route, { headers: headers, observe: "response" }).toPromise()
.then((res: HttpResponse<Status[]>) => {
const link = res.headers.get('Link');
let lastId = null;
if(link){
const maxId = link.split('max_id=')[1];
if(maxId){
lastId = maxId.split('>;')[0];
}
}
return new FavoriteResult(lastId, res.body);
});
}
2019-01-28 05:08:43 +01:00
2019-05-20 05:18:07 +02:00
searchAccount(account: AccountInfo, query: string, limit: number = 40, following: boolean = false, resolve = true): Promise<Account[]> {
const route = `https://${account.instance}${this.apiRoutes.searchForAccounts}?q=${query}&limit=${limit}&following=${following}&resolve=${resolve}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Account[]>(route, { headers: headers }).toPromise()
}
2018-10-04 02:13:31 +02:00
reblog(account: AccountInfo, status: Status): Promise<Status> {
const route = `https://${account.instance}${this.apiRoutes.reblogStatus}`.replace('{0}', status.id);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise()
}
2019-03-10 19:36:22 +01:00
unreblog(account: AccountInfo, status: Status): Promise<Status> {
2018-10-04 02:13:31 +02:00
const route = `https://${account.instance}${this.apiRoutes.unreblogStatus}`.replace('{0}', status.id);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise()
}
favorite(account: AccountInfo, status: Status): Promise<Status> {
2018-10-04 02:13:31 +02:00
const route = `https://${account.instance}${this.apiRoutes.favouritingStatus}`.replace('{0}', status.id);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise()
}
2019-03-10 19:36:22 +01:00
unfavorite(account: AccountInfo, status: Status): Promise<Status> {
2018-10-04 02:13:31 +02:00
const route = `https://${account.instance}${this.apiRoutes.unfavouritingStatus}`.replace('{0}', status.id);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise()
2019-03-23 22:43:59 +01:00
}
2019-03-10 22:38:10 +01:00
2019-02-23 05:35:12 +01:00
getRelationships(account: AccountInfo, accountsToRetrieve: Account[]): Promise<Relationship[]> {
2019-03-10 22:38:10 +01:00
let params = `?${this.formatArray(accountsToRetrieve.map(x => x.id.toString()), 'id')}`;
// accountsToRetrieve.forEach(x => {
// if (params.includes('id')) params += '&';
// params += `id[]=${x.id}`;
// });
2019-02-23 05:35:12 +01:00
const route = `https://${account.instance}${this.apiRoutes.getAccountRelationships}${params}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Relationship[]>(route, { headers: headers }).toPromise();
}
2019-03-10 19:36:22 +01:00
follow(currentlyUsedAccount: AccountInfo, account: Account): Promise<Relationship> {
2019-02-23 07:00:33 +01:00
const route = `https://${currentlyUsedAccount.instance}${this.apiRoutes.follow}`.replace('{0}', account.id.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${currentlyUsedAccount.token.access_token}` });
return this.httpClient.post<Relationship>(route, null, { headers: headers }).toPromise();
2019-03-10 19:36:22 +01:00
}
2019-02-23 07:00:33 +01:00
unfollow(currentlyUsedAccount: AccountInfo, account: Account): Promise<Relationship> {
const route = `https://${currentlyUsedAccount.instance}${this.apiRoutes.unfollow}`.replace('{0}', account.id.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${currentlyUsedAccount.token.access_token}` });
return this.httpClient.post<Relationship>(route, null, { headers: headers }).toPromise();
}
2019-03-10 19:36:22 +01:00
uploadMediaAttachment(account: AccountInfo, file: File, description: string): Promise<Attachment> {
2019-03-10 19:36:22 +01:00
let input = new FormData();
input.append('file', file);
input.append('description', description);
2019-03-10 19:36:22 +01:00
const route = `https://${account.instance}${this.apiRoutes.uploadMediaAttachment}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Attachment>(route, input, { headers: headers }).toPromise();
}
//TODO: add focus support
2019-03-23 22:43:59 +01:00
updateMediaAttachment(account: AccountInfo, mediaId: string, description: string): Promise<Attachment> {
2019-03-10 19:36:22 +01:00
let input = new FormData();
input.append('description', description);
const route = `https://${account.instance}${this.apiRoutes.updateMediaAttachment.replace('{0}', mediaId)}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.put<Attachment>(route, input, { headers: headers }).toPromise();
}
2019-03-10 22:38:10 +01:00
getNotifications(account: AccountInfo, excludeTypes: string[] = null, maxId: string = null, sinceId: string = null, limit: number = 15): Promise<Notification[]> {
let route = `https://${account.instance}${this.apiRoutes.getNotifications}?limit=${limit}`;
if(maxId){
route += `&max_id=${maxId}`;
}
if(sinceId){
route += `&since_id=${sinceId}`;
}
if(excludeTypes && excludeTypes.length > 0) {
const excludeTypeArray = this.formatArray(excludeTypes, 'exclude_types');
route += `&${excludeTypeArray}`;
}
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Notification[]>(route, { headers: headers }).toPromise();
}
2019-03-10 22:38:10 +01:00
private formatArray(data: string[], paramName: string): string {
let result = '';
data.forEach(x => {
2019-03-25 05:52:30 +01:00
if (result.includes(paramName)) result += '&';
2019-03-10 22:38:10 +01:00
result += `${paramName}[]=${x}`;
});
return result;
2019-05-19 02:44:36 +02:00
}
getLists(account: AccountInfo): Promise<StreamElement[]> {
let route = `https://${account.instance}${this.apiRoutes.getLists}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<List[]>(route, { headers: headers }).toPromise()
.then((lists: List[]) => {
const streams: StreamElement[] = [];
for (const list of lists) {
const stream = new StreamElement(StreamTypeEnum.list, list.title, account.id, null, list.title, list.id, account.instance);
streams.push(stream);
}
return streams;
});
2019-03-10 22:38:10 +01:00
}
2019-05-19 08:09:30 +02:00
createList(account: AccountInfo, title: string): Promise<StreamElement> {
let route = `https://${account.instance}${this.apiRoutes.postList}?title=${title}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<List>(route, null, { headers: headers }).toPromise()
.then((list: List) => {
return new StreamElement(StreamTypeEnum.list, list.title, account.id, null, list.title, list.id, account.instance);
});
}
2019-05-19 20:55:37 +02:00
deleteList(account: AccountInfo, listId: string): Promise<any> {
let route = `https://${account.instance}${this.apiRoutes.deleteList}`.replace('{0}', listId);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.delete(route, { headers: headers }).toPromise();
}
getListAccounts(account: AccountInfo, listId: string): Promise<Account[]> {
let route = `https://${account.instance}${this.apiRoutes.getAccountsInList}`.replace('{0}', listId);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Account[]>(route, { headers: headers }).toPromise();
}
addAccountToList(account: AccountInfo, listId: string, accountId: number): Promise<any> {
let route = `https://${account.instance}${this.apiRoutes.addAccountToList}`.replace('{0}', listId);
route += `?account_ids[]=${accountId}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post(route, null, { headers: headers }).toPromise();
}
2019-05-21 00:42:27 +02:00
removeAccountFromList(account: AccountInfo, listId: string, accountId: number): Promise<any> {
let route = `https://${account.instance}${this.apiRoutes.addAccountToList}`.replace('{0}', listId);
route += `?account_ids[]=${accountId}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.delete(route, { headers: headers }).toPromise();
}
2019-06-05 01:33:36 +02:00
voteOnPoll(account: AccountInfo, pollId: string, pollSelection: number[]): Promise<Poll> {
let route = `https://${account.instance}${this.apiRoutes.voteOnPoll}`.replace('{0}', pollId);
route += `?${this.formatArray(pollSelection.map(x => x.toString()), 'choices')}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Poll>(route, null, { headers: headers }).toPromise();
}
2019-06-08 04:15:36 +02:00
getPoll(account: AccountInfo, pollId: string): Promise<Poll> {
let route = `https://${account.instance}${this.apiRoutes.getPoll}`.replace('{0}', pollId);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.get<Poll>(route, { headers: headers }).toPromise();
2019-07-04 00:43:37 +02:00
}
mute(account: AccountInfo, accounId: number): Promise<Relationship> {
let route = `https://${account.instance}${this.apiRoutes.mute}`.replace('{0}', accounId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Relationship>(route, null, { headers: headers }).toPromise();
}
block(account: AccountInfo, accounId: number): Promise<Relationship> {
let route = `https://${account.instance}${this.apiRoutes.block}`.replace('{0}', accounId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Relationship>(route, null, { headers: headers }).toPromise();
}
pinOnProfile(account: AccountInfo, statusId: string): Promise<Status> {
let route = `https://${account.instance}${this.apiRoutes.pinStatus}`.replace('{0}', statusId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise();
}
unpinFromProfile(account: AccountInfo, statusId: string): Promise<Status> {
let route = `https://${account.instance}${this.apiRoutes.unpinStatus}`.replace('{0}', statusId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise();
}
muteConversation(account: AccountInfo, statusId: string): Promise<Status> {
let route = `https://${account.instance}${this.apiRoutes.muteStatus}`.replace('{0}', statusId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise();
}
unmuteConversation(account: AccountInfo, statusId: string): Promise<Status> {
let route = `https://${account.instance}${this.apiRoutes.unmuteStatus}`.replace('{0}', statusId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.post<Status>(route, null, { headers: headers }).toPromise();
}
deleteStatus(account: AccountInfo, statusId: string): Promise<any> {
let route = `https://${account.instance}${this.apiRoutes.deleteStatus}`.replace('{0}', statusId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
return this.httpClient.delete<any>(route, { headers: headers }).toPromise();
2019-07-30 03:05:37 +02:00
}
getCustomEmojis(account: AccountInfo): Promise<Emoji[]> {
let route = `https://${account.instance}${this.apiRoutes.getCustomEmojis}`;
return this.httpClient.get<Emoji[]>(route).toPromise();
}
2018-09-16 08:09:48 +02:00
}
2018-09-26 05:59:44 +02:00
export enum VisibilityEnum {
Unknown = 0,
Public = 1,
Unlisted = 2,
Private = 3,
Direct = 4
2019-03-10 22:38:10 +01:00
}
class StatusData {
status: string;
media_ids: string[];
in_reply_to_id: string;
sensitive: boolean;
spoiler_text: string;
visibility: string;
2019-03-23 22:43:59 +01:00
}
export class FavoriteResult {
constructor(
public max_id: string,
public favorites: Status[]) {}
2018-09-26 05:59:44 +02:00
}