Fix megalodon class for 3.0.0 in main

This commit is contained in:
AkiraFukushima 2020-03-15 17:47:40 +09:00
parent 6709156c7f
commit 5ab9c28ead
4 changed files with 161 additions and 118 deletions

View File

@ -1,5 +1,5 @@
import { isEmpty } from 'lodash'
import Mastodon, { Account as RemoteAccount, ProxyConfig } from 'megalodon'
import generator, { detector, Entity, ProxyConfig } from 'megalodon'
import Datastore from 'nedb'
import log from 'electron-log'
import { LocalAccount } from '~/src/types/localAccount'
@ -295,10 +295,11 @@ export default class Account {
* @return {LocalAccount} updated account
*/
async refresh(account: LocalAccount, proxy: ProxyConfig | false): Promise<LocalAccount> {
let client = new Mastodon(account.accessToken!, account.baseURL + '/api/v1', 'Whalebird', proxy)
const sns = await detector(account.baseURL, proxy)
let client = generator(sns, account.baseURL, account.accessToken, 'Whalebird', proxy)
let json = {}
try {
const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
const res = await client.verifyAccountCredentials()
json = {
username: res.data.username,
accountId: res.data.id,
@ -311,9 +312,9 @@ export default class Account {
if (!account.refreshToken) {
throw new RefreshTokenDoesNotExist()
}
const token = await Mastodon.refreshToken(account.clientId, account.clientSecret, account.refreshToken, account.baseURL, proxy)
client = new Mastodon(token.access_token, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
const token = await client.refreshToken(account.clientId, account.clientSecret, account.refreshToken)
client = generator(sns, account.baseURL, token.access_token, 'Whalebird', proxy)
const res = await client.verifyAccountCredentials()
json = {
username: res.data.username,
accountId: res.data.id,
@ -326,9 +327,14 @@ export default class Account {
}
// Confirm the access token, and check duplicate
async fetchAccount(account: LocalAccount, accessToken: string, proxy: ProxyConfig | false): Promise<RemoteAccount> {
const client = new Mastodon(accessToken, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
async fetchAccount(
sns: 'mastodon' | 'pleroma' | 'misskey',
account: LocalAccount,
accessToken: string,
proxy: ProxyConfig | false
): Promise<Entity.Account> {
const client = generator(sns, account.baseURL, accessToken, 'Whalebird', proxy)
const res = await client.verifyAccountCredentials()
const query = {
baseURL: account.baseURL,
username: res.data.username

View File

@ -1,10 +1,10 @@
import Mastodon, { OAuth, ProxyConfig } from 'megalodon'
import generator, { ProxyConfig, detector } from 'megalodon'
import Account from './account'
import { LocalAccount } from '~/src/types/localAccount'
const appName = 'Whalebird'
const appURL = 'https://whalebird.org'
const scope = 'read write follow'
const scopes = ['read', 'write', 'follow']
export default class Authentication {
private db: Account
@ -32,15 +32,12 @@ export default class Authentication {
async getAuthorizationUrl(domain = 'mastodon.social', proxy: ProxyConfig | false): Promise<string> {
this.setOtherInstance(domain)
const res = await Mastodon.registerApp(
appName,
{
scopes: scope,
website: appURL
},
this.baseURL,
proxy
)
const sns = await detector(this.baseURL, proxy)
const client = generator(sns, this.baseURL, null, 'Whalebird', proxy)
const res = await client.registerApp(appName, {
scopes: scopes,
website: appURL
})
this.clientId = res.clientId
this.clientSecret = res.clientSecret
@ -71,14 +68,9 @@ export default class Authentication {
}
async getAccessToken(code: string, proxy: ProxyConfig | false): Promise<string> {
const tokenData: OAuth.TokenData = await Mastodon.fetchAccessToken(
this.clientId,
this.clientSecret,
code,
this.baseURL,
'urn:ietf:wg:oauth:2.0:oob',
proxy
)
const sns = await detector(this.baseURL, proxy)
const client = generator(sns, this.baseURL, null, 'Whalebird', proxy)
const tokenData = await client.fetchAccessToken(this.clientId, this.clientSecret, code, 'urn:ietf:wg:oauth:2.0:oob')
const search = {
baseURL: this.baseURL,
domain: this.domain,
@ -88,7 +80,7 @@ export default class Authentication {
const rec = await this.db.searchAccount(search)
const accessToken = tokenData.accessToken
const refreshToken = tokenData.refreshToken
const data = await this.db.fetchAccount(rec, accessToken, proxy)
const data = await this.db.fetchAccount(sns, rec, accessToken, proxy)
await this.db.updateAccount(rec._id!, {
username: data.username,
accountId: data.id,

View File

@ -24,14 +24,14 @@ import path from 'path'
import ContextMenu from 'electron-context-menu'
import { initSplashScreen, Config } from '@trodi/electron-splashscreen'
import openAboutWindow from 'about-window'
import { Status, Notification as RemoteNotification, Account as RemoteAccount } from 'megalodon'
import { Entity, detector } from 'megalodon'
import sanitizeHtml from 'sanitize-html'
import AutoLaunch from 'auto-launch'
import pkg from '~/package.json'
import Authentication from './auth'
import Account from './account'
import WebSocket, { StreamingURL } from './websocket'
import { StreamingURL, UserStreaming, DirectStreaming, LocalStreaming, PublicStreaming, ListStreaming, TagStreaming } from './websocket'
import Preferences from './preferences'
import Fonts from './fonts'
import Hashtags from './hashtags'
@ -530,7 +530,7 @@ ipcMain.on('reset-badge', () => {
})
// user streaming
let userStreamings: { [key: string]: WebSocket | null } = {}
let userStreamings: { [key: string]: UserStreaming | null } = {}
ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<LocalAccount>) => {
accounts.map(async account => {
@ -543,10 +543,11 @@ ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<Lo
userStreamings[id] = null
}
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
userStreamings[id] = new WebSocket(acct, url, proxy)
userStreamings[id]!.startUserStreaming(
async (update: Status) => {
const sns = await detector(acct.baseURL, proxy)
const url = await StreamingURL(sns, acct, proxy)
userStreamings[id] = new UserStreaming(sns, acct, url, proxy)
userStreamings[id]!.start(
async (update: Entity.Status) => {
if (!event.sender.isDestroyed()) {
event.sender.send(`update-start-all-user-streamings-${id}`, update)
}
@ -557,7 +558,7 @@ ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<Lo
// Cache account
await accountCache.insertAccount(id, update.account.acct).catch(err => console.error(err))
},
(notification: RemoteNotification) => {
(notification: Entity.Notification) => {
const preferences = new Preferences(preferencesDBPath)
preferences.load().then(conf => {
const options = createNotification(notification, conf.notification.notify)
@ -641,7 +642,7 @@ type StreamingSetting = {
account: LocalAccount
}
let directMessagesStreaming: WebSocket | null = null
let directMessagesStreaming: DirectStreaming | null = null
ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, obj: StreamingSetting) => {
const { account } = obj
@ -654,11 +655,11 @@ ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, obj: St
directMessagesStreaming = null
}
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
directMessagesStreaming = new WebSocket(acct, url, proxy)
const sns = await detector(acct.baseURL, proxy)
const url = await StreamingURL(sns, acct, proxy)
directMessagesStreaming = new DirectStreaming(sns, acct, url, proxy)
directMessagesStreaming.start(
'direct',
(update: Status) => {
(update: Entity.Status) => {
if (!event.sender.isDestroyed()) {
event.sender.send('update-start-directmessages-streaming', update)
}
@ -690,7 +691,7 @@ ipcMain.on('stop-directmessages-streaming', () => {
}
})
let localStreaming: WebSocket | null = null
let localStreaming: LocalStreaming | null = null
ipcMain.on('start-local-streaming', async (event: IpcMainEvent, obj: StreamingSetting) => {
const { account } = obj
@ -703,11 +704,11 @@ ipcMain.on('start-local-streaming', async (event: IpcMainEvent, obj: StreamingSe
localStreaming = null
}
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
localStreaming = new WebSocket(acct, url, proxy)
const sns = await detector(acct.baseURL, proxy)
const url = await StreamingURL(sns, acct, proxy)
localStreaming = new LocalStreaming(sns, acct, url, proxy)
localStreaming.start(
'public:local',
(update: Status) => {
(update: Entity.Status) => {
if (!event.sender.isDestroyed()) {
event.sender.send('update-start-local-streaming', update)
}
@ -739,7 +740,7 @@ ipcMain.on('stop-local-streaming', () => {
}
})
let publicStreaming: WebSocket | null = null
let publicStreaming: PublicStreaming | null = null
ipcMain.on('start-public-streaming', async (event: IpcMainEvent, obj: StreamingSetting) => {
const { account } = obj
@ -752,11 +753,11 @@ ipcMain.on('start-public-streaming', async (event: IpcMainEvent, obj: StreamingS
publicStreaming = null
}
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
publicStreaming = new WebSocket(acct, url, proxy)
const sns = await detector(acct.baseURL, proxy)
const url = await StreamingURL(sns, acct, proxy)
publicStreaming = new PublicStreaming(sns, acct, url, proxy)
publicStreaming.start(
'public',
(update: Status) => {
(update: Entity.Status) => {
if (!event.sender.isDestroyed()) {
event.sender.send('update-start-public-streaming', update)
}
@ -788,7 +789,7 @@ ipcMain.on('stop-public-streaming', () => {
}
})
let listStreaming: WebSocket | null = null
let listStreaming: ListStreaming | null = null
type ListID = {
listID: string
@ -805,11 +806,12 @@ ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListID & Str
listStreaming = null
}
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
listStreaming = new WebSocket(acct, url, proxy)
const sns = await detector(acct.baseURL, proxy)
const url = await StreamingURL(sns, acct, proxy)
listStreaming = new ListStreaming(sns, acct, url, proxy)
listStreaming.start(
`list&list=${listID}`,
(update: Status) => {
listID,
(update: Entity.Status) => {
if (!event.sender.isDestroyed()) {
event.sender.send('update-start-list-streaming', update)
}
@ -841,7 +843,7 @@ ipcMain.on('stop-list-streaming', () => {
}
})
let tagStreaming: WebSocket | null = null
let tagStreaming: TagStreaming | null = null
type Tag = {
tag: string
@ -858,11 +860,12 @@ ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: Tag & Streami
tagStreaming = null
}
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
tagStreaming = new WebSocket(acct, url, proxy)
const sns = await detector(acct.baseURL, proxy)
const url = await StreamingURL(sns, acct, proxy)
tagStreaming = new TagStreaming(sns, acct, url, proxy)
tagStreaming.start(
`hashtag&tag=${tag}`,
(update: Status) => {
tag,
(update: Entity.Status) => {
if (!event.sender.isDestroyed()) {
event.sender.send('update-start-tag-streaming', update)
}
@ -1375,7 +1378,7 @@ async function reopenWindow() {
}
}
const createNotification = (notification: RemoteNotification, notifyConfig: Notify): NotificationConstructorOptions | null => {
const createNotification = (notification: Entity.Notification, notifyConfig: Notify): NotificationConstructorOptions | null => {
switch (notification.type) {
case 'favourite':
if (notifyConfig.favourite) {
@ -1422,7 +1425,7 @@ const createNotification = (notification: RemoteNotification, notifyConfig: Noti
return null
}
const username = (account: RemoteAccount): string => {
const username = (account: Entity.Account): string => {
if (account.display_name !== '') {
return account.display_name
} else {

View File

@ -1,73 +1,39 @@
import Mastodon, { WebSocket as SocketListener, Status, Notification, Instance, Response, ProxyConfig } from 'megalodon'
import generator, { MegalodonInterface, WebSocketInterface, Entity, ProxyConfig } from 'megalodon'
import log from 'electron-log'
import { LocalAccount } from '~/src/types/localAccount'
const StreamingURL = async (account: LocalAccount, proxy: ProxyConfig | false): Promise<string> => {
const StreamingURL = async (
sns: 'mastodon' | 'pleroma' | 'misskey',
account: LocalAccount,
proxy: ProxyConfig | false
): Promise<string> => {
if (!account.accessToken) {
throw new Error('access token is empty')
}
const client = new Mastodon(account.accessToken, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res: Response<Instance> = await client.get<Instance>('/instance')
const client = generator(sns, account.baseURL, account.accessToken, 'Whalebird', proxy)
const res = await client.getInstance()
return res.data.urls.streaming_api
}
export { StreamingURL }
export default class WebSocket {
private client: Mastodon
private listener: SocketListener | null
class WebSocket {
public client: MegalodonInterface
public listener: WebSocketInterface | null
constructor(account: LocalAccount, streamingURL: string, proxy: ProxyConfig | false) {
constructor(sns: 'mastodon' | 'pleroma' | 'misskey', account: LocalAccount, streamingURL: string, proxy: ProxyConfig | false) {
const url = streamingURL.replace(/^https:\/\//, 'wss://')
this.client = new Mastodon(account.accessToken!, url + '/api/v1', 'Whalebird', proxy)
this.client = generator(sns, url, account.accessToken, 'Whalebird', proxy)
this.listener = null
}
startUserStreaming(updateCallback: Function, notificationCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.socket('/streaming', 'user')
public bindListener(updateCallback: Function, deleteCallback: Function, errCallback: Function) {
if (!this.listener) {
log.error('listener does not exist')
return
}
this.listener.on('connect', _ => {
log.info('/streaming/?stream=user started')
})
this.listener.on('update', (status: Status) => {
updateCallback(status)
})
this.listener.on('notification', (notification: Notification) => {
notificationCallback(notification)
})
this.listener.on('delete', (id: string) => {
deleteCallback(id)
})
this.listener.on('error', (err: Error) => {
errCallback(err)
})
this.listener.on('parser-error', (err: Error) => {
errCallback(err)
})
}
/**
* Start new custom streaming with websocket.
* @param stream Path of streaming.
* @param updateCallback A callback function which is called update.
* @param errCallback A callback function which ic called error.
* When local timeline, the path is `public:local`.
* When public timeline, the path is `public`.
* When hashtag timeline, the path is `hashtag&tag=tag_name`.
* When list timeline, the path is `list&list=list_id`.
*/
start(stream: string, updateCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.socket('/streaming', stream)
this.listener.on('connect', _ => {
log.info(`/streaming/?stream=${stream} started`)
})
this.listener.on('update', (status: Status) => {
this.listener.on('update', (status: Entity.Status) => {
updateCallback(status)
})
@ -84,7 +50,7 @@ export default class WebSocket {
})
}
stop() {
public stop() {
if (this.listener) {
this.listener.removeAllListeners('connect')
this.listener.removeAllListeners('update')
@ -102,3 +68,79 @@ export default class WebSocket {
}
}
}
export class UserStreaming extends WebSocket {
public start(updateCallback: Function, notificationCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.userSocket()
this.listener.on('connect', _ => {
log.info('user streaming is started')
})
this.listener.on('notification', (notification: Entity.Notification) => {
notificationCallback(notification)
})
this.bindListener(updateCallback, deleteCallback, errCallback)
}
}
export class DirectStreaming extends WebSocket {
public start(updateCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.directSocket()
this.listener.on('connect', _ => {
log.info('direct streaming is started')
})
this.bindListener(updateCallback, deleteCallback, errCallback)
}
}
export class LocalStreaming extends WebSocket {
public start(updateCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.localSocket()
this.listener.on('connect', _ => {
log.info('local streaming is started')
})
this.bindListener(updateCallback, deleteCallback, errCallback)
}
}
export class PublicStreaming extends WebSocket {
public start(updateCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.publicSocket()
this.listener.on('connect', _ => {
log.info('public streaming is started')
})
this.bindListener(updateCallback, deleteCallback, errCallback)
}
}
export class ListStreaming extends WebSocket {
public start(listID: string, updateCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.listSocket(listID)
this.listener.on('connect', _ => {
log.info('list streaming is started')
})
this.bindListener(updateCallback, deleteCallback, errCallback)
}
}
export class TagStreaming extends WebSocket {
public start(tag: string, updateCallback: Function, deleteCallback: Function, errCallback: Function) {
this.listener = this.client.tagSocket(tag)
this.listener.on('connect', _ => {
log.info('tag streaming is started')
})
this.bindListener(updateCallback, deleteCallback, errCallback)
}
}