From 1c836dfb95d2cf4623b98072ceef4a026d04a294 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Mon, 29 Jul 2019 23:32:02 +0900 Subject: [PATCH] refs #983 Get streaming url for instance API before start streaming --- src/main/index.ts | 473 +++++++++++++++++++++--------------------- src/main/websocket.ts | 14 +- 2 files changed, 247 insertions(+), 240 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index e562f1a5..e86a552b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -28,7 +28,7 @@ import sanitizeHtml from 'sanitize-html' import pkg from '~/package.json' import Authentication from './auth' import Account from './account' -import WebSocket from './websocket' +import WebSocket, { StreamingURL } from './websocket' import Preferences from './preferences' import Fonts from './fonts' import Hashtags from './hashtags' @@ -441,79 +441,78 @@ ipcMain.on('reset-badge', () => { let userStreamings: { [key: string]: WebSocket | null } = {} ipcMain.on('start-all-user-streamings', (event: Event, accounts: Array) => { - accounts.map(account => { + accounts.map(async account => { const id: string = account._id! - accountManager - .getAccount(id) - .then(acct => { - // Stop old user streaming - if (userStreamings[id]) { - userStreamings[id]!.stop() - userStreamings[id] = null - } - userStreamings[id] = new WebSocket(acct) - userStreamings[id]!.startUserStreaming( - (update: Status) => { - if (!event.sender.isDestroyed()) { - event.sender.send(`update-start-all-user-streamings-${id}`, update) - } - }, - (notification: RemoteNotification) => { - const preferences = new Preferences(preferencesDBPath) - preferences.load().then(conf => { - const options = createNotification(notification, conf.notification.notify) - if (options !== null) { - const notify = new Notification(options) - notify.on('click', _ => { - if (!event.sender.isDestroyed()) { - event.sender.send('open-notification-tab', id) - } - }) - notify.show() - } - }) - if (process.platform === 'darwin') { - app.dock.setBadge('•') + try { + const acct = await accountManager.getAccount(id) + // Stop old user streaming + if (userStreamings[id]) { + userStreamings[id]!.stop() + userStreamings[id] = null + } + const url = await StreamingURL(acct) + userStreamings[id] = new WebSocket(acct, url) + userStreamings[id]!.startUserStreaming( + (update: Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send(`update-start-all-user-streamings-${id}`, update) + } + }, + (notification: RemoteNotification) => { + const preferences = new Preferences(preferencesDBPath) + preferences.load().then(conf => { + const options = createNotification(notification, conf.notification.notify) + if (options !== null) { + const notify = new Notification(options) + notify.on('click', _ => { + if (!event.sender.isDestroyed()) { + event.sender.send('open-notification-tab', id) + } + }) + notify.show() } + }) + if (process.platform === 'darwin') { + app.dock.setBadge('•') + } - // In macOS and Windows, sometimes window is closed (not quit). - // But streamings are always running. - // When window is closed, we can not send event to webContents; because it is already destroyed. - // So we have to guard it. - if (!event.sender.isDestroyed()) { - // To update notification timeline - event.sender.send(`notification-start-all-user-streamings-${id}`, notification) + // In macOS and Windows, sometimes window is closed (not quit). + // But streamings are always running. + // When window is closed, we can not send event to webContents; because it is already destroyed. + // So we have to guard it. + if (!event.sender.isDestroyed()) { + // To update notification timeline + event.sender.send(`notification-start-all-user-streamings-${id}`, notification) - // Does not exist a endpoint for only mention. And mention is a part of notification. - // So we have to get mention from notification. - if (notification.type === 'mention') { - event.sender.send(`mention-start-all-user-streamings-${id}`, notification) - } - } - }, - (statusId: string) => { - if (!event.sender.isDestroyed()) { - event.sender.send(`delete-start-all-user-streamings-${id}`, statusId) - } - }, - (err: Error) => { - log.error(err) - // In macOS, sometimes window is closed (not quit). - // When window is closed, we can not send event to webContents; because it is destroyed. - // So we have to guard it. - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-all-user-streamings', err) + // Does not exist a endpoint for only mention. And mention is a part of notification. + // So we have to get mention from notification. + if (notification.type === 'mention') { + event.sender.send(`mention-start-all-user-streamings-${id}`, notification) } } - ) - }) - .catch((err: Error) => { - log.error(err) - const streamingError = new StreamingError(err.message, account.domain) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-all-user-streamings', streamingError) + }, + (statusId: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send(`delete-start-all-user-streamings-${id}`, statusId) + } + }, + (err: Error) => { + log.error(err) + // In macOS, sometimes window is closed (not quit). + // When window is closed, we can not send event to webContents; because it is destroyed. + // So we have to guard it. + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-all-user-streamings', err) + } } - }) + ) + } catch (err) { + log.error(err) + const streamingError = new StreamingError(err.message, account.domain) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-all-user-streamings', streamingError) + } + } }) }) @@ -545,44 +544,44 @@ type StreamingSetting = { let directMessagesStreaming: WebSocket | null = null -ipcMain.on('start-directmessages-streaming', (event: Event, obj: StreamingSetting) => { +ipcMain.on('start-directmessages-streaming', async (event: Event, obj: StreamingSetting) => { const { account } = obj - accountManager - .getAccount(account._id!) - .then(acct => { - // Stop old directmessages streaming - if (directMessagesStreaming !== null) { - directMessagesStreaming.stop() - directMessagesStreaming = null - } + try { + const acct = await accountManager.getAccount(account._id!) - directMessagesStreaming = new WebSocket(acct) - directMessagesStreaming.start( - 'direct', - (update: Status) => { - if (!event.sender.isDestroyed()) { - event.sender.send('update-start-directmessages-streaming', update) - } - }, - (id: string) => { - if (!event.sender.isDestroyed()) { - event.sender.send('delete-start-directmessages-streaming', id) - } - }, - (err: Error) => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-directmessages-streaming', err) - } + // Stop old directmessages streaming + if (directMessagesStreaming !== null) { + directMessagesStreaming.stop() + directMessagesStreaming = null + } + + const url = await StreamingURL(acct) + directMessagesStreaming = new WebSocket(acct, url) + directMessagesStreaming.start( + 'direct', + (update: Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send('update-start-directmessages-streaming', update) + } + }, + (id: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send('delete-start-directmessages-streaming', id) + } + }, + (err: Error) => { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-directmessages-streaming', err) } - ) - }) - .catch(err => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-directmessages-streaming', err) } - }) + ) + } catch (err) { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-directmessages-streaming', err) + } + } }) ipcMain.on('stop-directmessages-streaming', () => { @@ -594,44 +593,44 @@ ipcMain.on('stop-directmessages-streaming', () => { let localStreaming: WebSocket | null = null -ipcMain.on('start-local-streaming', (event: Event, obj: StreamingSetting) => { +ipcMain.on('start-local-streaming', async (event: Event, obj: StreamingSetting) => { const { account } = obj - accountManager - .getAccount(account._id!) - .then(acct => { - // Stop old local streaming - if (localStreaming !== null) { - localStreaming.stop() - localStreaming = null - } + try { + const acct = await accountManager.getAccount(account._id!) - localStreaming = new WebSocket(acct) - localStreaming.start( - 'public:local', - (update: Status) => { - if (!event.sender.isDestroyed()) { - event.sender.send('update-start-local-streaming', update) - } - }, - (id: string) => { - if (!event.sender.isDestroyed()) { - event.sender.send('delete-start-local-streaming', id) - } - }, - (err: Error) => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-local-streaming', err) - } + // Stop old local streaming + if (localStreaming !== null) { + localStreaming.stop() + localStreaming = null + } + + const url = await StreamingURL(acct) + localStreaming = new WebSocket(acct, url) + localStreaming.start( + 'public:local', + (update: Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send('update-start-local-streaming', update) + } + }, + (id: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send('delete-start-local-streaming', id) + } + }, + (err: Error) => { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-local-streaming', err) } - ) - }) - .catch(err => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-local-streaming', err) } - }) + ) + } catch (err) { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-local-streaming', err) + } + } }) ipcMain.on('stop-local-streaming', () => { @@ -643,44 +642,44 @@ ipcMain.on('stop-local-streaming', () => { let publicStreaming: WebSocket | null = null -ipcMain.on('start-public-streaming', (event: Event, obj: StreamingSetting) => { +ipcMain.on('start-public-streaming', async (event: Event, obj: StreamingSetting) => { const { account } = obj - accountManager - .getAccount(account._id!) - .then(acct => { - // Stop old public streaming - if (publicStreaming !== null) { - publicStreaming.stop() - publicStreaming = null - } + try { + const acct = await accountManager.getAccount(account._id!) - publicStreaming = new WebSocket(acct) - publicStreaming.start( - 'public', - (update: Status) => { - if (!event.sender.isDestroyed()) { - event.sender.send('update-start-public-streaming', update) - } - }, - (id: string) => { - if (!event.sender.isDestroyed()) { - event.sender.send('delete-start-public-streaming', id) - } - }, - (err: Error) => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-public-streaming', err) - } + // Stop old public streaming + if (publicStreaming !== null) { + publicStreaming.stop() + publicStreaming = null + } + + const url = await StreamingURL(acct) + publicStreaming = new WebSocket(acct, url) + publicStreaming.start( + 'public', + (update: Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send('update-start-public-streaming', update) + } + }, + (id: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send('delete-start-public-streaming', id) + } + }, + (err: Error) => { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-public-streaming', err) } - ) - }) - .catch(err => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-public-streaming', err) } - }) + ) + } catch (err) { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-public-streaming', err) + } + } }) ipcMain.on('stop-public-streaming', () => { @@ -696,44 +695,44 @@ type ListID = { listID: string } -ipcMain.on('start-list-streaming', (event: Event, obj: ListID & StreamingSetting) => { +ipcMain.on('start-list-streaming', async (event: Event, obj: ListID & StreamingSetting) => { const { listID, account } = obj - accountManager - .getAccount(account._id!) - .then(acct => { - // Stop old list streaming - if (listStreaming !== null) { - listStreaming.stop() - listStreaming = null - } + try { + const acct = await accountManager.getAccount(account._id!) - listStreaming = new WebSocket(acct) - listStreaming.start( - `list&list=${listID}`, - (update: Status) => { - if (!event.sender.isDestroyed()) { - event.sender.send('update-start-list-streaming', update) - } - }, - (id: string) => { - if (!event.sender.isDestroyed()) { - event.sender.send('delete-start-list-streaming', id) - } - }, - (err: Error) => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-list-streaming', err) - } + // Stop old list streaming + if (listStreaming !== null) { + listStreaming.stop() + listStreaming = null + } + + const url = await StreamingURL(acct) + listStreaming = new WebSocket(acct, url) + listStreaming.start( + `list&list=${listID}`, + (update: Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send('update-start-list-streaming', update) + } + }, + (id: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send('delete-start-list-streaming', id) + } + }, + (err: Error) => { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-list-streaming', err) } - ) - }) - .catch(err => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-list-streaming', err) } - }) + ) + } catch (err) { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-list-streaming', err) + } + } }) ipcMain.on('stop-list-streaming', () => { @@ -749,44 +748,44 @@ type Tag = { tag: string } -ipcMain.on('start-tag-streaming', (event: Event, obj: Tag & StreamingSetting) => { +ipcMain.on('start-tag-streaming', async (event: Event, obj: Tag & StreamingSetting) => { const { tag, account } = obj - accountManager - .getAccount(account._id!) - .then(acct => { - // Stop old tag streaming - if (tagStreaming !== null) { - tagStreaming.stop() - tagStreaming = null - } + try { + const acct = await accountManager.getAccount(account._id!) - tagStreaming = new WebSocket(acct) - tagStreaming.start( - `hashtag&tag=${tag}`, - (update: Status) => { - if (!event.sender.isDestroyed()) { - event.sender.send('update-start-tag-streaming', update) - } - }, - (id: string) => { - if (!event.sender.isDestroyed()) { - event.sender.send('delete-start-tag-streaming', id) - } - }, - (err: Error) => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-tag-streaming', err) - } + // Stop old tag streaming + if (tagStreaming !== null) { + tagStreaming.stop() + tagStreaming = null + } + + const url = await StreamingURL(acct) + tagStreaming = new WebSocket(acct, url) + tagStreaming.start( + `hashtag&tag=${tag}`, + (update: Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send('update-start-tag-streaming', update) + } + }, + (id: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send('delete-start-tag-streaming', id) + } + }, + (err: Error) => { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-tag-streaming', err) } - ) - }) - .catch(err => { - log.error(err) - if (!event.sender.isDestroyed()) { - event.sender.send('error-start-tag-streaming', err) } - }) + ) + } catch (err) { + log.error(err) + if (!event.sender.isDestroyed()) { + event.sender.send('error-start-tag-streaming', err) + } + } }) ipcMain.on('stop-tag-streaming', () => { diff --git a/src/main/websocket.ts b/src/main/websocket.ts index 15b0683a..e98e426c 100644 --- a/src/main/websocket.ts +++ b/src/main/websocket.ts @@ -1,13 +1,21 @@ -import Mastodon, { WebSocket as SocketListener, Status, Notification } from 'megalodon' +import Mastodon, { WebSocket as SocketListener, Status, Notification, Instance } from 'megalodon' import log from 'electron-log' import { LocalAccount } from '~/src/types/localAccount' +const StreamingURL = async (account: LocalAccount): Promise => { + const client = new Mastodon(account.accessToken!, account.baseURL + '/api/v1') + const res = await client.get('/instance') + return res.data.urls.streaming_api +} + +export { StreamingURL } + export default class WebSocket { private client: Mastodon private listener: SocketListener | null - constructor(account: LocalAccount) { - const url = account.baseURL.replace(/^https:\/\//, 'wss://') + constructor(account: LocalAccount, streamingURL: string) { + const url = streamingURL.replace(/^https:\/\//, 'wss://') this.client = new Mastodon(account.accessToken!, url + '/api/v1') this.listener = null }