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

315 lines
11 KiB
TypeScript
Raw Normal View History

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
2019-03-01 04:54:26 +01:00
2019-11-08 05:05:48 +01:00
import { Status, Notification } from "./models/mastodon.interfaces";
import { ApiRoutes } from "./models/api.settings";
2018-11-03 19:12:14 +01:00
import { StreamTypeEnum, StreamElement } from "../states/streams.state";
import { MastodonWrapperService } from "./mastodon-wrapper.service";
import { AccountInfo } from "../states/accounts.state";
2023-04-23 22:09:44 +02:00
import { InstanceInfo, ToolsService } from "./tools.service";
@Injectable()
export class StreamingService {
2018-11-24 05:04:02 +01:00
public readonly nbStatusPerIteration: number = 20;
constructor(
2023-04-23 22:09:44 +02:00
private readonly mastodonService: MastodonWrapperService,
private readonly toolsService: ToolsService) { }
2019-11-08 05:39:26 +01:00
getStreaming(accountInfo: AccountInfo, stream: StreamElement, since_id: string = null): StreamingWrapper {
2019-11-08 05:05:48 +01:00
//new EventSourceStreaminWrapper(accountInfo, stream);
2019-09-12 06:08:28 +02:00
2023-04-23 22:09:44 +02:00
return new StreamingWrapper(this.mastodonService, this.toolsService, accountInfo, stream, this.nbStatusPerIteration);
}
}
export class StreamingWrapper {
statusUpdateSubjet = new BehaviorSubject<StatusUpdate>(null);
eventSource: WebSocket;
private apiRoutes = new ApiRoutes();
2018-11-24 05:04:02 +01:00
private errorClosing: boolean;
private since_id: string;
private since_id_notifications: string;
2018-11-24 05:04:02 +01:00
private disposed: boolean;
constructor(
private readonly mastodonService: MastodonWrapperService,
2023-04-23 22:09:44 +02:00
private readonly toolsService: ToolsService,
2018-11-03 19:12:14 +01:00
private readonly account: AccountInfo,
private readonly stream: StreamElement,
2019-11-08 05:39:26 +01:00
private readonly nbStatusPerIteration: number,
since_id: string = null) {
2019-11-08 05:39:26 +01:00
this.since_id = since_id;
2019-10-04 02:05:27 +02:00
this.start(account, stream);
}
2018-09-13 08:02:24 +02:00
2018-11-24 05:04:02 +01:00
dispose(): any {
this.disposed = true;
this.eventSource.close();
}
2019-10-04 02:05:27 +02:00
private start(account: AccountInfo, stream: StreamElement) {
this.mastodonService.refreshAccountIfNeeded(account)
.catch(err => {
return account;
})
.then((refreshedAccount: AccountInfo) => {
2023-04-23 22:09:44 +02:00
let getInstanceProms = this.toolsService.getInstanceInfo(refreshedAccount);
return getInstanceProms.then(inst => {
return new StreamingAccountInfo(inst, refreshedAccount);
});
})
.then((account: StreamingAccountInfo) => {
const route = this.getRoute(account.instanceInfo, account.refreshedAccount, stream);
2019-10-04 02:05:27 +02:00
this.eventSource = new WebSocket(route);
this.eventSource.onmessage = x => {
if (x.data !== '') {
this.statusParsing(<WebSocketEvent>JSON.parse(x.data));
}
}
this.eventSource.onerror = x => this.webSocketGotError(x);
this.eventSource.onopen = x => { };
2023-04-23 22:09:44 +02:00
this.eventSource.onclose = x => this.webSocketClosed(account.refreshedAccount, stream, x);
2019-11-14 04:03:56 +01:00
});
2018-09-16 08:09:48 +02:00
}
private webSocketGotError(x: Event) {
2018-09-16 08:09:48 +02:00
this.errorClosing = true;
}
2019-10-04 02:05:27 +02:00
private webSocketClosed(account: AccountInfo, stream: StreamElement, x: Event) {
if (this.errorClosing) {
2019-07-04 06:36:43 +02:00
setTimeout(() => {
2019-11-14 04:03:56 +01:00
if (stream.type === StreamTypeEnum.personnal) {
2019-11-08 05:39:26 +01:00
this.pullNewNotifications();
this.pullNewStatuses();
2019-11-08 05:39:26 +01:00
} else {
this.pullNewStatuses();
2019-11-14 04:03:56 +01:00
}
2019-07-04 06:36:43 +02:00
this.errorClosing = false;
}, 60 * 1000);
2018-11-24 05:04:02 +01:00
} else if (!this.disposed) {
2019-10-04 02:05:27 +02:00
setTimeout(() => { this.start(account, stream) }, 60 * 1000);
}
}
2019-11-14 04:03:56 +01:00
private pullNewNotifications() {
2023-08-06 10:41:10 +02:00
this.mastodonService.getNotifications(this.account, [], null, this.since_id_notifications, 10)
2019-11-08 05:39:26 +01:00
.then((notifications: Notification[]) => {
2019-11-08 06:56:44 +01:00
//notifications = notifications.sort((a, b) => a.id.localeCompare(b.id));
let soundMuted = !this.since_id_notifications;
2019-11-14 04:03:56 +01:00
2019-11-08 06:56:44 +01:00
notifications = notifications.reverse();
2019-11-08 05:39:26 +01:00
for (const n of notifications) {
2019-11-14 04:03:56 +01:00
2019-11-08 05:39:26 +01:00
const update = new StatusUpdate();
update.notification = n;
update.type = EventEnum.notification;
2019-11-14 04:03:56 +01:00
update.muteSound = soundMuted;
this.since_id_notifications = n.id;
2019-11-08 05:39:26 +01:00
this.statusUpdateSubjet.next(update);
}
})
.catch(err => {
console.error(err);
})
.then(() => {
if (!this.disposed) {
setTimeout(() => { this.pullNewNotifications() }, 60 * 1000);
}
});
}
2019-10-04 02:05:27 +02:00
private pullNewStatuses() {
2019-05-19 02:44:36 +02:00
this.mastodonService.getTimeline(this.account, this.stream.type, null, this.since_id, this.nbStatusPerIteration, this.stream.tag, this.stream.listId)
2018-11-03 19:12:14 +01:00
.then((status: Status[]) => {
// status = status.sort((n1, n2) => { return (<number>n1.id) < (<number>n2.id); });
status = status.sort((a, b) => a.id.localeCompare(b.id));
for (const s of status) {
const update = new StatusUpdate();
update.status = s;
update.type = EventEnum.update;
this.since_id = update.status.id;
this.statusUpdateSubjet.next(update);
}
})
.catch(err => {
console.error(err);
})
.then(() => {
// setTimeout(() => { this.start(domain) }, 20 * 1000);
2018-11-24 05:04:02 +01:00
if (!this.disposed) {
2019-10-04 02:05:27 +02:00
setTimeout(() => { this.pullNewStatuses() }, 60 * 1000);
2018-11-24 05:04:02 +01:00
}
2018-11-03 19:12:14 +01:00
});
}
2018-09-13 08:02:24 +02:00
2018-09-16 08:09:48 +02:00
private statusParsing(event: WebSocketEvent) {
const newUpdate = new StatusUpdate();
switch (event.event) {
case 'update':
newUpdate.type = EventEnum.update;
newUpdate.status = <Status>JSON.parse(event.payload);
break;
case 'delete':
newUpdate.type = EventEnum.delete;
newUpdate.messageId = event.payload;
2019-07-06 04:27:40 +02:00
newUpdate.account = this.account;
break;
2019-11-08 05:05:48 +01:00
case 'notification':
newUpdate.type = EventEnum.notification;
2019-11-14 04:03:56 +01:00
newUpdate.notification = <Notification>JSON.parse(event.payload);
break;
default:
newUpdate.type = EventEnum.unknow;
}
this.statusUpdateSubjet.next(newUpdate);
}
2018-09-16 08:09:48 +02:00
2023-04-23 22:09:44 +02:00
private getRoute(instanceInfo: InstanceInfo, account: AccountInfo, stream: StreamElement): string {
let streamingEndpoint = `wss://${account.instance}`;
if(instanceInfo.major >= 4){
streamingEndpoint = instanceInfo.streamingApi;
}
2018-11-03 19:12:14 +01:00
const streamingRouteType = this.getStreamingRouteType(stream.type);
2023-04-23 22:09:44 +02:00
let route = `${streamingEndpoint}${this.apiRoutes.getStreaming}`.replace('{0}', account.token.access_token).replace('{1}', streamingRouteType);
2018-11-03 19:12:14 +01:00
2018-11-24 05:04:02 +01:00
if (stream.tag) route = `${route}&tag=${stream.tag}`;
2019-05-19 02:44:36 +02:00
if (stream.list) route = `${route}&list=${stream.listId}`;
2018-11-03 19:12:14 +01:00
return route;
}
private getStreamingRouteType(type: StreamTypeEnum): string {
switch (type) {
case StreamTypeEnum.global:
return 'public';
case StreamTypeEnum.local:
return 'public:local';
case StreamTypeEnum.personnal:
return 'user';
2018-11-03 19:12:14 +01:00
case StreamTypeEnum.directmessages:
return 'direct';
case StreamTypeEnum.tag:
return 'hashtag';
case StreamTypeEnum.list:
return 'list';
default:
throw Error('Not supported');
}
}
2018-09-13 08:02:24 +02:00
}
2019-09-12 06:08:28 +02:00
export class EventSourceStreaminWrapper {
eventSource: EventSource;
private apiRoutes = new ApiRoutes();
constructor(
private readonly account: AccountInfo,
private readonly stream: StreamElement
2019-11-14 04:03:56 +01:00
) {
2019-09-12 06:08:28 +02:00
this.start();
}
2019-11-14 04:03:56 +01:00
private start() {
2019-09-12 06:08:28 +02:00
const route = this.getRoute();
this.eventSource = new EventSource(route);
this.eventSource.addEventListener('update', u => {
console.warn('update');
console.warn(u);
});
this.eventSource.addEventListener('delete', d => {
console.warn('delete');
console.warn(d);
});
this.eventSource.onmessage = x => {
console.log(x);
2019-11-14 04:03:56 +01:00
if (x.data !== '') {
2019-09-12 06:08:28 +02:00
this.onMessage(JSON.parse(x.data));
}
};
this.eventSource.onerror = x => {
this.onError(x);
2019-11-14 04:03:56 +01:00
};
2019-09-12 06:08:28 +02:00
console.warn('this.eventSource.CONNECTING');
console.warn(this.eventSource.CONNECTING);
console.warn('this.eventSource.OPEN');
console.warn(this.eventSource.OPEN);
2019-11-14 04:03:56 +01:00
}
2019-09-12 06:08:28 +02:00
private onMessage(data) {
console.warn('onMessage');
console.warn(data);
}
private onError(data) {
console.warn('onError');
console.warn(data);
}
private getRoute(): string {
const streamingRouteType = this.getStreamingRouteType(this.stream.type);
let route = `https://${this.account.instance}/api/v1/streaming/${streamingRouteType}?access_token=${this.account.token.access_token}`;
return route;
}
private getStreamingRouteType(type: StreamTypeEnum): string {
switch (type) {
case StreamTypeEnum.global:
return 'public';
case StreamTypeEnum.local:
return 'public/local';
case StreamTypeEnum.personnal:
return 'user';
case StreamTypeEnum.directmessages:
return 'direct';
case StreamTypeEnum.tag:
return 'hashtag?tag={0}';
case StreamTypeEnum.list:
return 'list?list={0}';
case StreamTypeEnum.directmessages:
return 'direct';
default:
throw Error('Not supported');
}
}
}
2018-09-13 08:02:24 +02:00
class WebSocketEvent {
event: string;
payload: any;
2018-09-13 08:02:24 +02:00
}
2023-04-23 22:09:44 +02:00
class StreamingAccountInfo {
constructor(
public instanceInfo: InstanceInfo,
public refreshedAccount: AccountInfo) {
}
}
2018-09-13 08:02:24 +02:00
export class StatusUpdate {
type: EventEnum;
status: Status;
2019-07-06 04:27:40 +02:00
messageId: string;
account: AccountInfo;
2019-11-08 05:05:48 +01:00
notification: Notification;
2019-11-14 04:03:56 +01:00
muteSound: boolean;
2018-09-13 08:02:24 +02:00
}
export enum EventEnum {
unknow = 0,
update = 1,
2019-11-08 05:05:48 +01:00
delete = 2,
notification = 3
}