diff --git a/chat/Chat.vue b/chat/Chat.vue index 44e961b..2dfef62 100644 --- a/chat/Chat.vue +++ b/chat/Chat.vue @@ -124,16 +124,14 @@ import {InlineDisplayMode} from '../interfaces'; } }); core.connection.onEvent('closed', (isReconnect) => { - if(process.env.NODE_ENV !== 'production') { - log.debug( - 'connection.closed', - { - character: core.characters.ownCharacter?.name, - error: this.error, - isReconnect - } - ); - } + log.debug( + 'connection.closed', + { + character: core.characters.ownCharacter?.name, + error: this.error, + isReconnect + } + ); if(isReconnect) (this.$refs['reconnecting']).show(true); if(this.connected) core.notifications.playSound('logout'); @@ -148,14 +146,12 @@ import {InlineDisplayMode} from '../interfaces'; core.connection.onEvent('connecting', async() => { this.connecting = true; - if(process.env.NODE_ENV !== 'production') { - log.debug( - 'connection.connecting', - { - character: core.characters.ownCharacter?.name - } - ); - } + log.debug( + 'connection.connecting', + { + character: core.characters.ownCharacter?.name + } + ); profileApiInit({ defaultCharacter: this.defaultCharacter, animateEicons: core.state.settings.animatedEicons, fuzzyDates: true, @@ -164,14 +160,12 @@ import {InlineDisplayMode} from '../interfaces'; if(core.state.settings.notifications) await core.notifications.requestPermission(); }); core.connection.onEvent('connected', () => { - if(process.env.NODE_ENV !== 'production') { - log.debug( - 'connection.connected', - { - character: core.characters.ownCharacter?.name - } - ); - } + log.debug( + 'connection.connected', + { + character: core.characters.ownCharacter?.name + } + ); (this.$refs['reconnecting']).hide(); this.error = ''; @@ -184,15 +178,13 @@ import {InlineDisplayMode} from '../interfaces'; document.title = (hasNew ? '💬 ' : '') + l(core.connection.isOpen ? 'title.connected' : 'title', core.connection.character); }); core.connection.onError((e) => { - if(process.env.NODE_ENV !== 'production') { - log.debug( - 'connection.error', - { - error: errorToString(e), - character: core.characters.ownCharacter?.name - } - ); - } + log.debug( + 'connection.error', + { + error: errorToString(e), + character: core.characters.ownCharacter?.name + } + ); if((e).request !== undefined) {//catch axios network errors this.error = l('login.connectError', errorToString(e)); diff --git a/chat/ads/ad-coordinator-guest.ts b/chat/ads/ad-coordinator-guest.ts index 425cedc..0262f3d 100644 --- a/chat/ads/ad-coordinator-guest.ts +++ b/chat/ads/ad-coordinator-guest.ts @@ -3,9 +3,15 @@ import { ipcRenderer, IpcRendererEvent } from 'electron'; import log from 'electron-log'; //tslint:disable-line:match-default-export-name import core from '../core'; +interface PendingAd { + resolve(): void, + reject(err: Error): void, + from: number; +} + export class AdCoordinatorGuest { - protected pendingAds: Record = {}; + protected pendingAds: Record = {}; protected adCounter = 0; constructor() { @@ -26,7 +32,7 @@ export class AdCoordinatorGuest { } - requestTurnToPostAd(): Promise { + async requestTurnToPostAd(): Promise { return new Promise( (resolve, reject) => { const adId = `${Math.round(Math.random() * 1000000)}-${this.adCounter++}-${Date.now()}`; @@ -42,7 +48,7 @@ export class AdCoordinatorGuest { clear(): void { - _.each(this.pendingAds, (pa) => (pa.reject())); + _.each(this.pendingAds, (pa) => (pa.reject(new Error('Pending ad cleared')))); console.debug('adid.clear', _.keys(this.pendingAds), core.characters.ownCharacter?.name); diff --git a/chat/ads/ad-manager.ts b/chat/ads/ad-manager.ts index 028d409..4651e5b 100644 --- a/chat/ads/ad-manager.ts +++ b/chat/ads/ad-manager.ts @@ -69,19 +69,17 @@ export class AdManager { const delayTime = Date.now(); - if (process.env.NODE_ENV !== 'production') { - log.debug( - 'adManager.sendAdToChannel', - { - character: core.characters.ownCharacter?.name, - channel: conv.channel.name, - throatDelta: throatTime - initTime, - delayDelta: delayTime - throatTime, - totalWait: delayTime - initTime, - msg - } - ); - } + log.debug( + 'adManager.sendAdToChannel', + { + character: core.characters.ownCharacter?.name, + channel: conv.channel.name, + throatDelta: throatTime - initTime, + delayDelta: delayTime - throatTime, + totalWait: delayTime - initTime, + msg + } + ); await conv.sendAd(msg); } diff --git a/chat/conversations.ts b/chat/conversations.ts index 822504d..c0c96ef 100644 --- a/chat/conversations.ts +++ b/chat/conversations.ts @@ -415,19 +415,17 @@ class ChannelConversation extends Conversation implements Interfaces.ChannelConv core.connection.send('LRP', {channel: this.channel.id, message: text}); core.cache.markLastPostTime(); - if (process.env.NODE_ENV !== 'production') { - log.debug( - 'conversation.sendAd', - { - character: core.characters.ownCharacter?.name, - channel: this.channel.name, - throatDelta: throatTime - initTime, - delayDelta: delayTime - throatTime, - totalWait: delayTime - initTime, - text - } - ); - } + log.debug( + 'conversation.sendAd', + { + character: core.characters.ownCharacter?.name, + channel: this.channel.name, + throatDelta: throatTime - initTime, + delayDelta: delayTime - throatTime, + totalWait: delayTime - initTime, + text + } + ); await this.addMessage( createMessage(MessageType.Ad, core.characters.ownCharacter, text, new Date()) diff --git a/fchat/connection.ts b/fchat/connection.ts index 19c4c1e..1378a99 100644 --- a/fchat/connection.ts +++ b/fchat/connection.ts @@ -4,6 +4,7 @@ import {Connection as Interfaces, WebSocketConnection} from './interfaces'; import ReadyState = WebSocketConnection.ReadyState; import log from 'electron-log'; //tslint:disable-line:match-default-export-name import core from '../chat/core'; +import throat from 'throat'; const fatalErrors = [2, 3, 4, 9, 30, 31, 33, 39, 40, 62, -4]; const dieErrors = [9, 30, 31, 39, 40]; @@ -11,12 +12,12 @@ const dieErrors = [9, 30, 31, 39, 40]; let lastFetch = Date.now(); let lastApiTicketFetch = Date.now(); +const queryApiThroat = throat(2); +const queryTicketThroat = throat(1); + async function queryApi(this: void, endpoint: string, data: object): Promise { - if (false) { - console.log(`https://www.f-list.net/json/api/${endpoint}, gap: ${Date.now() - lastFetch}ms`); - lastFetch = Date.now(); - } + lastFetch = Date.now(); return Axios.post(`https://www.f-list.net/json/api/${endpoint}`, qs.stringify(data)); } @@ -115,13 +116,59 @@ export default class Connection implements Interfaces.Connection { if(!keepState) this.character = ''; } + get isOpen(): boolean { return this.socket !== undefined && this.socket.readyState === ReadyState.OPEN; } + async queryApi(endpoint: string, data?: {account?: string, ticket?: string}): Promise { + return queryApiThroat(async() => this.queryApiExec(endpoint, data)); + } + + + protected async refreshTicket(oldTicket: string): Promise { + if (this.ticket !== oldTicket) { + log.debug( + 'api.ticket.renew.resolve.cache', + { + character: core.characters.ownCharacter?.name + } + ); + + return this.ticket; + } + + if (!this.ticketProvider) { + throw new Error('No credentials set!'); + } + + this.ticket = await queryTicketThroat(async() => this.ticketProvider!()); + + log.debug( + 'api.ticket.renew.resolve.refresh', + { + character: core.characters.ownCharacter?.name + } + ); + + return this.ticket; + } + + + protected async queryApiExec(endpoint: string, data?: {account?: string, ticket?: string}): Promise { if(!this.ticketProvider) throw new Error('No credentials set!'); + log.debug( + 'api.query.start', + { + endpoint, + character: core.characters.ownCharacter?.name, + deltaToLastApiCall: Date.now() - lastFetch, + deltaToLastApiTicket: Date.now() - lastApiTicketFetch + } + ); + if(data === undefined) data = {}; data.account = this.account; @@ -140,13 +187,13 @@ export default class Connection implements Interfaces.Connection { } ); - data.ticket = this.ticket = await this.ticketProvider(); + data.ticket = await this.refreshTicket(data.ticket); res = (await queryApi(endpoint, data)).data; } if(res.error !== '') { log.debug( - 'error.api.query', + 'api.query.error', { error: res.error, endpoint, @@ -160,6 +207,17 @@ export default class Connection implements Interfaces.Connection { (error).request = true; throw error; } + + log.debug( + 'api.query.success', + { + endpoint, + character: core.characters.ownCharacter?.name, + deltaToLastApiCall: Date.now() - lastFetch, + deltaToLastApiTicket: Date.now() - lastApiTicketFetch + } + ); + return res; } @@ -232,43 +290,48 @@ export default class Connection implements Interfaces.Connection { private async getTicket(password: string): Promise { console.log('Acquiring new API ticket'); + const oldLastApiTicketFetch = lastApiTicketFetch; - if(process.env.NODE_ENV !== 'production') { - console.log(`https://www.f-list.net/json/getApiTicket.php, gap: ${Date.now() - lastApiTicketFetch}ms`); + log.debug( + 'api.getTicket.start', + { + character: core.characters.ownCharacter?.name, + deltaToLastApiCall: Date.now() - lastFetch, + deltaToLastApiTicket: Date.now() - oldLastApiTicketFetch + } + ); - log.debug( - 'api.getTicket', - { - character: core.characters.ownCharacter?.name, - deltaToLastApiCall: Date.now() - lastFetch, - deltaToLastApiTicket: Date.now() - lastApiTicketFetch - } - ); - - lastApiTicketFetch = Date.now(); - } + lastApiTicketFetch = Date.now(); const data = <{ticket?: string, error: string}>(await Axios.post('https://www.f-list.net/json/getApiTicket.php', qs.stringify( {account: this.account, password, no_friends: true, no_bookmarks: true, no_characters: true}))).data; - if(data.ticket !== undefined) return data.ticket; - - if(process.env.NODE_ENV !== 'production') { - console.error('API Ticket Error', data.error); - - log.error( - 'error.api.getTicket', + if(data.ticket !== undefined) { + log.debug( + 'api.getTicket.success', { - character: core.characters.ownCharacter.name, - error: data.error, + character: core.characters.ownCharacter?.name, deltaToLastApiCall: Date.now() - lastFetch, - deltaToLastApiTicket: Date.now() - lastApiTicketFetch + deltaToLastApiTicket: Date.now() - oldLastApiTicketFetch } ); - lastApiTicketFetch = Date.now(); + return data.ticket; } + + console.error('API Ticket Error', data.error); + + log.error( + 'error.api.getTicket', + { + character: core.characters.ownCharacter.name, + error: data.error, + deltaToLastApiCall: Date.now() - lastFetch, + deltaToLastApiTicket: Date.now() - oldLastApiTicketFetch + } + ); + throw new Error(data.error); } diff --git a/mobile/chat.ts b/mobile/chat.ts index dabfda5..9312897 100644 --- a/mobile/chat.ts +++ b/mobile/chat.ts @@ -51,4 +51,4 @@ initCore(connection, Logs, SettingsStore, Notifications); new Index({ //tslint:disable-line:no-unused-expression el: '#app' -}); \ No newline at end of file +}); diff --git a/site/character_page/character_page.vue b/site/character_page/character_page.vue index 311cf44..f515abb 100644 --- a/site/character_page/character_page.vue +++ b/site/character_page/character_page.vue @@ -295,10 +295,14 @@ async updateMeta(name: string): Promise { - await this.updateImages(); - await this.updateGuestbook(); - await this.updateFriends(); - await this.updateGroups(); + await Promise.all( + [ + this.updateImages(), + this.updateGuestbook(), + this.updateFriends(), + this.updateGroups() + ] + ); await core.cache.profileCache.registerMeta( name,