Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mistdon v1.5.1a (Bluesky Alpha) #80

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
178 changes: 167 additions & 11 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const fs = require('fs')
const http = require('http')
const stateKeeper = require('electron-window-state')
const FeedParser = require('feedparser')
const AsyncLock = require('async-lock')
const fetch = require('node-fetch')

// アプリ保持用設定データの管理
Expand All @@ -22,8 +23,11 @@ var pref_temptl_fav = null
var cache_history = null
var cache_draft = null
var cache_emoji_history = null
var cache_bsky_session = new Map()
var oauth_session = null

var lock = new AsyncLock()

const is_windows = process.platform === 'win32'
const is_mac = process.platform === 'darwin'

Expand All @@ -48,11 +52,16 @@ async function readPrefAccs() {
const content = readFile('app_prefs/auth.json')
if (!content) return null // ファイルが見つからなかったらnullを返却

pref_accounts = jsonToMap(JSON.parse(content), (elm) => `@${elm.user_id}@${elm.domain}`)
pref_accounts = jsonToMap(JSON.parse(content), getAccountKey)
console.log('@INF: read app_prefs/auth.json.')
return pref_accounts
}

function getAccountKey(json) {
if (json.platform == 'Bluesky') return `@${json.user_id}`
else return `@${json.user_id}@${json.domain}`
}

/**
* #IPC
* アカウント認証情報を設定ファイルに書き込む(Mastodon用)
Expand Down Expand Up @@ -83,7 +92,7 @@ async function writePrefMstdAccs(event, json_data) {
// キャッシュを更新
if (!pref_accounts) {
// キャッシュがない場合はファイルを読み込んでキャッシュを生成
pref_accounts = jsonToMap(JSON.parse(content), (elm) => `@${elm.user_id}@${elm.domain}`)
pref_accounts = jsonToMap(JSON.parse(content), getAccountKey)
} else {
pref_accounts.set(`@${json_data.user_id}@${json_data.domain}`, write_json)
}
Expand Down Expand Up @@ -124,12 +133,51 @@ async function writePrefMskyAccs(event, json_data) {
// キャッシュを更新
if (!pref_accounts) {
// キャッシュがない場合はファイルを読み込んでキャッシュを生成
pref_accounts = jsonToMap(JSON.parse(content), (elm) => `@${elm.user_id}@${elm.domain}`)
pref_accounts = jsonToMap(JSON.parse(content), getAccountKey)
} else {
pref_accounts.set(`@${write_json.user_id}@${write_json.domain}`, write_json)
}
}

async function writePrefBskyAccs(event, json_data) {
// JSONを生成(あとでキャッシュに入れるので)
const write_json = {
'domain': json_data.domain,
'platform': 'Bluesky',
'user_id': json_data.user_id,
'username': json_data.username,
'socket_url': null,
'client_id': json_data.did,
'client_secret': json_data.app_pass,
'access_token': null,
'avatar_url': json_data.avatar_url,
'post_maxlength': json_data.post_maxlength,
'acc_color': getRandomColor()
}

// ユーザー情報をファイルに書き込み
const content = await writeFileArrayJson('app_prefs/auth.json', write_json)

// ユーザー情報のキャッシュを更新
if (!pref_accounts) {
// キャッシュがない場合はファイルを読み込んでキャッシュを生成
pref_accounts = jsonToMap(JSON.parse(content), getAccountKey)
} else {
pref_accounts.set(`@${json_data.user_id}`, write_json)
}

// セッション情報のJSONを生成
const write_session = {
'handle': json_data.user_id,
'pds': json_data.domain,
'refresh_token': json_data.refresh_token,
'access_token': json_data.access_token
}

// セッションキャッシュを更新
cache_bsky_session.set(json_data.user_id, write_session)
}

/**
* #IPC
* アカウント認証情報に色情報を書き込む.
Expand Down Expand Up @@ -159,7 +207,7 @@ async function writePrefAccColor(event, json_data) {
const content = await overwriteFile('app_prefs/auth.json', write_json)

// キャッシュを更新
pref_accounts = jsonToMap(JSON.parse(content), (elm) => `@${elm.user_id}@${elm.domain}`)
pref_accounts = jsonToMap(JSON.parse(content), getAccountKey)
}

/**
Expand Down Expand Up @@ -291,7 +339,7 @@ async function authorizeMastodon(auth_code) {
// キャッシュを更新
if (!pref_accounts) {
// キャッシュがない場合はファイルを読み込んでキャッシュを生成
pref_accounts = jsonToMap(JSON.parse(content), (elm) => `@${elm.user_id}@${elm.domain}`)
pref_accounts = jsonToMap(JSON.parse(content), getAccountKey)
} else {
pref_accounts.set(`@${write_json.user_id}@${write_json.domain}`, write_json)
}
Expand Down Expand Up @@ -337,7 +385,7 @@ async function authorizeMisskey(session) {
// キャッシュを更新
if (!pref_accounts) {
// キャッシュがない場合はファイルを読み込んでキャッシュを生成
pref_accounts = jsonToMap(JSON.parse(content), (elm) => `@${elm.user_id}@${elm.domain}`)
pref_accounts = jsonToMap(JSON.parse(content), getAccountKey)
} else {
pref_accounts.set(`@${write_json.user_id}@${write_json.domain}`, write_json)
}
Expand All @@ -347,6 +395,90 @@ async function authorizeMisskey(session) {
}
}

async function refreshBlueskySession(event, handle) {
console.log("#BSKY-SESSION: Exclusive Bluesky Session getted...")
return await lock.acquire('bluesky-session', async () => { // 同時実行しないよう排他
const session = cache_bsky_session.get(handle)

if (session) { // セッションキャッシュが残っている場合はセッションが生きてるかの確認から
try {
const session_info = await ajax({ // セッションが有効か確認
method: "GET",
url: `https://${session.pds}/xrpc/com.atproto.server.getSession`,
headers: { 'Authorization': `Bearer ${session.access_token}` }
})

// 有効なセッションの場合はキャッシュされたアクセストークンを返す
console.log("#BSKY-SESSION: Access token returned.")
return session.access_token
} catch (err) {
if (err.message != '400') { // Bad Request以外はトークンの取得に失敗
console.log(err)
return null
}
}

try {
const session_info = await ajax({ // セッションを更新する
method: "POST",
url: `https://${session.pds}/xrpc/com.atproto.server.refreshSession`,
headers: { 'Authorization': `Bearer ${session.refresh_token}` }
})

// セッション情報のJSONを生成
const write_session = {
'handle': session.handle,
'pds': session.pds,
'refresh_token': session_info.body.refreshJwt,
'access_token': session_info.body.accessJwt
}

// セッション情報のキャッシュを更新
cache_bsky_session.set(write_session.handle, write_session)

console.log("#BSKY-SESSION: Refresh token created.")
return session_info.body.accessJwt
} catch (err) {
if (err.message != '400') { // Bad Request以外はトークンの取得に失敗
console.log(err)
return null
}
}
}

try { // セッションを再取得する
const account = pref_accounts.get(`@${handle}`)
const pds = account.domain
const session_info = await ajax({
method: "POST",
url: `https://${pds}/xrpc/com.atproto.server.createSession`,
headers: { "Content-Type": "application/json" },
data: JSON.stringify({
'identifier': handle,
'password': account.client_secret
})
})

// セッション情報のJSONを生成
const write_session = {
'handle': handle,
'pds': pds,
'refresh_token': session_info.body.refreshJwt,
'access_token': session_info.body.accessJwt
}

// セッション情報をアプリにキャッシュする
cache_bsky_session.set(write_session.handle, write_session)

console.log("#BSKY-SESSION: Session Regenerated.")
return session_info.body.accessJwt
} catch (err) {
console.log(err)
}
return null
})
}

/**
* #IPC
* 保存してあるカラム設定情報を読み込む
Expand Down Expand Up @@ -576,6 +708,24 @@ function getAPIParams(event, arg) {
}
socket_url = `wss://${arg.host}/streaming`
break
case 'Bluesky': // Bluesky
// タイムラインタイプによって設定値を変える
switch (arg.timeline.timeline_type) {
case 'home': // ホームタイムライン
rest_url = `https://${arg.host}/xrpc/app.bsky.feed.getTimeline`
query_param = {}
socket_param = {}
break
case 'notification': // 通知
rest_url = `https://${arg.host}/xrpc/app.bsky.notification.listNotifications`
query_param = {}
socket_param = {}
break
default:
break
}
socket_url = null
break
default:
break
}
Expand Down Expand Up @@ -977,18 +1127,21 @@ async function ajax(arg) {
if (arg.method == "GET") { // GETはパラメータをURLに埋め込む
const query_param = Object.keys(arg.data).reduce((str, key) => `${str}&${key}=${arg.data[key]}`, '')
url += `?${query_param.substring(1)}`
} else { // POSTはパラメータをURLSearchParamsにセットする
const post_params = new URLSearchParams()
Object.keys(arg.data).forEach(key => post_params.append(key, arg.data[key]))
param.body = post_params
} else {
if (arg.headers['Content-Type'] == 'application/json') param.body = arg.data
else { // POSTはパラメータをURLSearchParamsにセットする
const post_params = new URLSearchParams()
Object.keys(arg.data).forEach(key => post_params.append(key, arg.data[key]))
param.body = post_params
}
}
}

// fetchでHTTP Requestを送信
response = await fetch(url, param)

// ステータスコードがエラーの場合はエラーを投げる
if (!response.ok) throw new Error(`HTTP Status: ${response.status}`)
if (!response.ok) throw new Error(response.status)

// responseをjsonとheaderとHTTP Statusに分けて返却
return {
Expand Down Expand Up @@ -1183,6 +1336,7 @@ app.whenReady().then(() => {
ipcMain.handle('read-emoji-history', readEmojiHistory)
ipcMain.on('write-pref-mstd-accs', writePrefMstdAccs)
ipcMain.on('write-pref-msky-accs', writePrefMskyAccs)
ipcMain.on('write-pref-bsky-accs', writePrefBskyAccs)
ipcMain.on('write-pref-acc-color', writePrefAccColor)
ipcMain.on('write-pref-cols', writePrefCols)
ipcMain.on('write-general-pref', writeGeneralPref)
Expand All @@ -1197,6 +1351,8 @@ app.whenReady().then(() => {
ipcMain.on('open-external-browser', openExternalBrowser)
ipcMain.on('notification', notification)

ipcMain.handle('refresh-bsky-session', refreshBlueskySession)

// ウィンドウ生成
createWindow()
app.on('activate', () => {
Expand Down
12 changes: 9 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"dependencies": {
"@electron-forge/publisher-github": "^6.4.2",
"async-lock": "^1.4.1",
"electron-squirrel-startup": "^1.0.0",
"electron-window-state": "^5.0.3",
"feedparser": "^2.2.10",
Expand Down
5 changes: 4 additions & 1 deletion preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ contextBridge.exposeInMainWorld('accessApi', {
readEmojiHistory: () => ipcRenderer.invoke('read-emoji-history'),
writePrefMstdAccs: (json_data) => ipcRenderer.send('write-pref-mstd-accs', json_data),
writePrefMskyAccs: (json_data) => ipcRenderer.send('write-pref-msky-accs', json_data),
writePrefBskyAccs: (json_data) => ipcRenderer.send('write-pref-bsky-accs', json_data),
writePrefAccColor: (json_data) => ipcRenderer.send('write-pref-acc-color', json_data),
writePrefCols: (json_data) => ipcRenderer.send('write-pref-cols', json_data),
writeGeneralPref: (json_data) => ipcRenderer.send('write-general-pref', json_data),
Expand All @@ -23,5 +24,7 @@ contextBridge.exposeInMainWorld('accessApi', {
fetchVersion: () => ipcRenderer.invoke('fetch-version'),
openOAuthSession: (json_data) => ipcRenderer.send('open-oauth', json_data),
openExternalBrowser: (url) => ipcRenderer.send('open-external-browser', url),
notification: (arg) => ipcRenderer.send('notification', arg)
notification: (arg) => ipcRenderer.send('notification', arg),

refreshBlueskySession: (handle) => ipcRenderer.invoke('refresh-bsky-session', handle)
})
Loading