Refactored with better posting rules
This commit is contained in:
		
							parent
							
								
									cf91fe1627
								
							
						
					
					
						commit
						75f099ce89
					
				| @ -70,14 +70,15 @@ | |||||||
|                 @click="hideSearch"><i class="fas fa-times"></i></a> |                 @click="hideSearch"><i class="fas fa-times"></i></a> | ||||||
|         </div> |         </div> | ||||||
|         <div class="auto-ads" v-show="isAutopostingAds()"> |         <div class="auto-ads" v-show="isAutopostingAds()"> | ||||||
|             <h4>Auto-Posting Ads</h4> |             <h4>{{l('admgr.activeHeader')}}</h4> | ||||||
|             <div class="update">{{adAutoPostUpdate}}</div> |             <div class="update">{{adAutoPostUpdate}}</div> | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|             <div v-show="adAutoPostNextAd" class="next"> |             <div v-show="adAutoPostNextAd" class="next"> | ||||||
|                 <h5>Coming Next</h5> |                 <h5>{{l('admgr.comingNext')}}</h5> | ||||||
|                 <div>{{(adAutoPostNextAd ? adAutoPostNextAd.substr(0, 50) : '')}}...</div> |                 <div>{{(adAutoPostNextAd ? adAutoPostNextAd.substr(0, 50) : '')}}...</div> | ||||||
|             </div> |             </div> | ||||||
|  | 
 | ||||||
|  |             <a class="btn btn-sm btn-outline-primary renew-autoposts" @click="renewAutoPosting()">{{l('admgr.renew')}}</a> | ||||||
|         </div> |         </div> | ||||||
|         <div class="border-top messages" :class="isChannel(conversation) ? 'messages-' + conversation.mode : undefined" ref="messages" |         <div class="border-top messages" :class="isChannel(conversation) ? 'messages-' + conversation.mode : undefined" ref="messages" | ||||||
|             @scroll="onMessagesScroll" style="flex:1;overflow:auto;margin-top:2px"> |             @scroll="onMessagesScroll" style="flex:1;overflow:auto;margin-top:2px"> | ||||||
| @ -117,15 +118,15 @@ | |||||||
|                 <ul class="nav nav-pills send-ads-switcher" v-if="isChannel(conversation)" |                 <ul class="nav nav-pills send-ads-switcher" v-if="isChannel(conversation)" | ||||||
|                     style="position:relative;z-index:10;margin-right:5px"> |                     style="position:relative;z-index:10;margin-right:5px"> | ||||||
|                     <li class="nav-item"> |                     <li class="nav-item"> | ||||||
|                         <a href="#" :class="{active: !conversation.isSendingAds, disabled: conversation.channel.mode != 'both'}" |                         <a href="#" :class="{active: !conversation.isSendingAds, disabled: (conversation.channel.mode != 'both') || (conversation.adManager.isActive())}" | ||||||
|                             class="nav-link" @click.prevent="setSendingAds(false)">{{l('channel.mode.chat')}}</a> |                             class="nav-link" @click.prevent="setSendingAds(false)">{{l('channel.mode.chat')}}</a> | ||||||
|                     </li> |                     </li> | ||||||
|                     <li class="nav-item"> |                     <li class="nav-item"> | ||||||
|                         <a href="#" :class="{active: conversation.isSendingAds, disabled: conversation.channel.mode != 'both'}" |                         <a href="#" :class="{active: conversation.isSendingAds, disabled: (conversation.channel.mode != 'both') || (conversation.adManager.isActive())}" | ||||||
|                             class="nav-link" @click.prevent="setSendingAds(true)">{{adsMode}}</a> |                             class="nav-link" @click.prevent="setSendingAds(true)">{{adsMode}}</a> | ||||||
|                     </li> |                     </li> | ||||||
|                     <li class="nav-item"> |                     <li class="nav-item"> | ||||||
|                         <a href="#" :class="{active: conversation.adState.active}" class="nav-link" @click="autoPostAds()">Auto-Post Ads</a> |                         <a href="#" :class="{active: conversation.adManager.isActive()}" class="nav-link toggle-autopost" @click="toggleAutoPostAds()">{{l('admgr.toggleAutoPost')}}</a> | ||||||
|                     </li> |                     </li> | ||||||
|                 </ul> |                 </ul> | ||||||
|                 <div class="btn btn-sm btn-primary" v-show="!settings.enterSend" @click="sendButton">{{l('chat.send')}}</div> |                 <div class="btn btn-sm btn-primary" v-show="!settings.enterSend" @click="sendButton">{{l('chat.send')}}</div> | ||||||
| @ -146,7 +147,7 @@ | |||||||
|     import {Keys} from '../keys'; |     import {Keys} from '../keys'; | ||||||
|     import {BBCodeView, Editor} from './bbcode'; |     import {BBCodeView, Editor} from './bbcode'; | ||||||
|     import CommandHelp from './CommandHelp.vue'; |     import CommandHelp from './CommandHelp.vue'; | ||||||
|     import { AdState, characterImage, getByteLength, getKey } from "./common"; |     import { characterImage, getByteLength, getKey } from "./common"; | ||||||
|     import ConversationSettings from './ConversationSettings.vue'; |     import ConversationSettings from './ConversationSettings.vue'; | ||||||
|     import core from './core'; |     import core from './core'; | ||||||
|     import {Channel, channelModes, Character, Conversation, Settings} from './interfaces'; |     import {Channel, channelModes, Character, Conversation, Settings} from './interfaces'; | ||||||
| @ -236,7 +237,8 @@ | |||||||
|                 setAdCountdown(); |                 setAdCountdown(); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             this.$watch('conversation.adState.active', () => (this.refreshAutoPostingTimer())); |             this.$watch(() => this.conversation.adManager.isActive(), () => (this.refreshAutoPostingTimer())); | ||||||
|  |             this.refreshAutoPostingTimer(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @Hook('destroyed') |         @Hook('destroyed') | ||||||
| @ -273,6 +275,7 @@ | |||||||
|             if(!anyDialogsShown) (<Editor>this.$refs['textBox']).focus(); |             if(!anyDialogsShown) (<Editor>this.$refs['textBox']).focus(); | ||||||
|             this.$nextTick(() => setTimeout(() => this.messageView.scrollTop = this.messageView.scrollHeight)); |             this.$nextTick(() => setTimeout(() => this.messageView.scrollTop = this.messageView.scrollHeight)); | ||||||
|             this.scrolledDown = true; |             this.scrolledDown = true; | ||||||
|  |             this.refreshAutoPostingTimer(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @Watch('conversation.messages') |         @Watch('conversation.messages') | ||||||
| @ -399,78 +402,29 @@ | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         isAutopostingAds(): boolean { |         isAutopostingAds(): boolean { | ||||||
|             return this.conversation.adState.active; |             return this.conversation.adManager.isActive(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         clearAutoPostAds(): void { |         stopAutoPostAds(): void { | ||||||
|             if (this.conversation.adState.interval) { |             this.conversation.adManager.stop(); | ||||||
|                 clearTimeout(this.conversation.adState.interval); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             this.conversation.adState = new AdState(); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         autoPostAds(): void { |         renewAutoPosting(): void { | ||||||
|             if(this.isAutopostingAds()) { |             this.conversation.adManager.renew(); | ||||||
|                 this.clearAutoPostAds(); | 
 | ||||||
|             this.refreshAutoPostingTimer(); |             this.refreshAutoPostingTimer(); | ||||||
|                 return; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|             const conversation = this.conversation; |  | ||||||
| 
 | 
 | ||||||
|             /**** Do not use 'this' keyword below this line, it will operate differently than you expect ****/ |         toggleAutoPostAds(): void { | ||||||
| 
 |             if(this.isAutopostingAds()) { | ||||||
|             const chanConv = (<Conversation.ChannelConversation>conversation); |                 this.stopAutoPostAds(); | ||||||
| 
 |             } else { | ||||||
|             const adState = conversation.adState; |                 this.conversation.adManager.start(); | ||||||
|             const initialWait = Math.max(0, chanConv.nextAd - Date.now()) * 1.1; |  | ||||||
| 
 |  | ||||||
|             adState.adIndex = 0; |  | ||||||
| 
 |  | ||||||
|             const sendNextPost = async () => { |  | ||||||
|                 const ads = conversation.settings.adSettings.ads; |  | ||||||
|                 const index = (adState.adIndex || 0); |  | ||||||
| 
 |  | ||||||
|                 if ((ads.length === 0) || ((adState.expireDue) && (adState.expireDue.getTime() < Date.now()))) { |  | ||||||
|                     conversation.adState = new AdState(); |  | ||||||
|                     return; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|                 const msg = ads[index % ads.length]; |  | ||||||
| 
 |  | ||||||
|                 await chanConv.sendAd(msg); |  | ||||||
| 
 |  | ||||||
|                 const nextInMs = Math.max(0, (chanConv.nextAd - Date.now())) * 1.1; |  | ||||||
| 
 |  | ||||||
|                 adState.adIndex = index + 1; |  | ||||||
|                 adState.nextPostDue = new Date(Date.now() + nextInMs); |  | ||||||
| 
 |  | ||||||
|                 adState.interval = setTimeout( |  | ||||||
|                     async () => { |  | ||||||
|                         await sendNextPost(); |  | ||||||
|                     }, |  | ||||||
|                     nextInMs |  | ||||||
|                 ); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|             adState.active = true; |  | ||||||
|             adState.nextPostDue = new Date(Date.now() + initialWait); |  | ||||||
|             adState.expireDue = new Date(Date.now() + 2 * 60 * 60 * 1000); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|             adState.interval = setTimeout( |  | ||||||
|                 async () => { |  | ||||||
|                     adState.firstPost = new Date(); |  | ||||||
| 
 |  | ||||||
|                     await sendNextPost(); |  | ||||||
|                 }, |  | ||||||
|                 initialWait |  | ||||||
|             ); |  | ||||||
| 
 |  | ||||||
|             this.refreshAutoPostingTimer(); |             this.refreshAutoPostingTimer(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -487,25 +441,28 @@ | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const updateAutoPostingState = () => { |             const updateAutoPostingState = () => { | ||||||
|                 const adState = this.conversation.adState; |                 const adManager = this.conversation.adManager; | ||||||
|                 const ads = this.conversation.settings.adSettings.ads; |  | ||||||
| 
 | 
 | ||||||
|                 if(ads.length > 0) { |                 this.adAutoPostNextAd = adManager.getNextAd() || null; | ||||||
|                     this.adAutoPostNextAd = ads[(adState.adIndex || 0) % ads.length]; |  | ||||||
| 
 | 
 | ||||||
|                     const diff = ((adState.nextPostDue || new Date()).getTime() - Date.now()) / 1000; |                 if(this.adAutoPostNextAd) { | ||||||
|                     const expDiff = ((adState.expireDue || new Date()).getTime() - Date.now()) / 1000; |                     const diff = ((adManager.getNextPostDue() || new Date()).getTime() - Date.now()) / 1000; | ||||||
|  |                     const expDiff = ((adManager.getExpireDue() || new Date()).getTime() - Date.now()) / 1000; | ||||||
| 
 | 
 | ||||||
|                     if((adState.nextPostDue) && (!adState.firstPost)) { |                     const diffMins = Math.floor(diff / 60); | ||||||
|                         this.adAutoPostUpdate = `Posting beings in ${Math.floor(diff / 60)}m ${Math.floor(diff % 60)}s`; |                     const diffSecs = Math.floor(diff % 60); | ||||||
|                     } else { |                     const expDiffMins = Math.floor(expDiff / 60); | ||||||
|                         this.adAutoPostUpdate = `Next ad in ${Math.floor(diff / 60)}m ${Math.floor(diff % 60)}s`; |                     const expDiffSecs = Math.floor(expDiff % 60); | ||||||
|                     } |  | ||||||
| 
 | 
 | ||||||
|                     this.adAutoPostUpdate += `, auto-posting expires in ${Math.floor(expDiff / 60)}m ${Math.floor(expDiff % 60)}s`; |                     this.adAutoPostUpdate = l( | ||||||
|  |                         ((adManager.getNextPostDue()) && (!adManager.getFirstPost())) ? 'admgr.postingBegins' : 'admgr.nextPostDue', | ||||||
|  |                         diffMins, | ||||||
|  |                         diffSecs | ||||||
|  |                     ) + l('admgr.expiresIn', expDiffMins, expDiffSecs); | ||||||
|                 } else { |                 } else { | ||||||
|                     this.adAutoPostNextAd = null; |                     this.adAutoPostNextAd = null; | ||||||
|                     this.adAutoPostUpdate = 'No ads have been set up -- auto-posting will be cancelled.'; | 
 | ||||||
|  |                     this.adAutoPostUpdate = l('admgr.noAds'); | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
| @ -514,7 +471,6 @@ | |||||||
|             updateAutoPostingState(); |             updateAutoPostingState(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         hasSFC(message: Conversation.Message): message is Conversation.SFCMessage { |         hasSFC(message: Conversation.Message): message is Conversation.SFCMessage { | ||||||
|             return (<Partial<Conversation.SFCMessage>>message).sfc !== undefined; |             return (<Partial<Conversation.SFCMessage>>message).sfc !== undefined; | ||||||
|         } |         } | ||||||
| @ -559,6 +515,12 @@ | |||||||
|             padding: 3px 10px; |             padding: 3px 10px; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |         .toggle-autopost { | ||||||
|  |             margin-left: 1px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|         .auto-ads { |         .auto-ads { | ||||||
|             background-color: rgba(255, 128, 32, 0.8); |             background-color: rgba(255, 128, 32, 0.8); | ||||||
|             padding-left: 10px; |             padding-left: 10px; | ||||||
| @ -566,6 +528,29 @@ | |||||||
|             padding-top: 5px; |             padding-top: 5px; | ||||||
|             padding-bottom: 5px; |             padding-bottom: 5px; | ||||||
|             margin: 0; |             margin: 0; | ||||||
|  |             position: relative; | ||||||
|  | 
 | ||||||
|  |             .renew-autoposts { | ||||||
|  |                 display: block; | ||||||
|  |                 float: right; | ||||||
|  |                 /* margin-top: auto; */ | ||||||
|  |                 /* margin-bottom: auto; */ | ||||||
|  |                 position: absolute; | ||||||
|  |                 /* bottom: 1px; */ | ||||||
|  |                 right: 10px; | ||||||
|  |                 top: 50%; | ||||||
|  |                 transform: translateY(-50%); | ||||||
|  |                 border-color: rgba(255, 255, 255, 0.5); | ||||||
|  |                 color: rgba(255, 255, 255, 0.9); | ||||||
|  | 
 | ||||||
|  |                 &:hover { | ||||||
|  |                     background-color: rgba(255, 255, 255, 0.3); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 &:active { | ||||||
|  |                     background-color: rgba(255, 255, 255, 0.6); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             h4 { |             h4 { | ||||||
|                 font-size: 1.1rem; |                 font-size: 1.1rem; | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import {WebSocketConnection} from '../fchat'; | import {WebSocketConnection} from '../fchat'; | ||||||
| 
 | 
 | ||||||
| export default class Socket implements WebSocketConnection { | export default class Socket implements WebSocketConnection { | ||||||
|     static host = 'wss://chat.f-list.net:9799'; |     static host = 'wss://chat.f-list.net/chat2'; | ||||||
|     private socket: WebSocket; |     private socket: WebSocket; | ||||||
|     private lastHandler: Promise<void> = Promise.resolve(); |     private lastHandler: Promise<void> = Promise.resolve(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										120
									
								
								chat/ad-manager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								chat/ad-manager.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | |||||||
|  | import { Conversation } from './interfaces'; | ||||||
|  | 
 | ||||||
|  | export class AdManager { | ||||||
|  |     static readonly POSTING_PERIOD = 3 * 60 * 60 * 1000; | ||||||
|  |     static readonly START_VARIANCE = 3 * 60 * 1000; | ||||||
|  |     static readonly POST_VARIANCE = 10 * 60 * 1000; | ||||||
|  |     static readonly POST_DELAY = 2 * 60 * 1000; | ||||||
|  | 
 | ||||||
|  |     private conversation: Conversation; | ||||||
|  | 
 | ||||||
|  |     private adIndex = 0; | ||||||
|  |     private active = false; | ||||||
|  |     private nextPostDue?: Date; | ||||||
|  |     private expireDue?: Date; | ||||||
|  |     private firstPost?: Date; | ||||||
|  |     private interval?: any; | ||||||
|  | 
 | ||||||
|  |     constructor(conversation: Conversation) { | ||||||
|  |         this.conversation = conversation; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     isActive(): boolean { | ||||||
|  |         return this.active; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private async sendNextPost(): Promise<void> { | ||||||
|  |         const msg = this.getNextAd(); | ||||||
|  | 
 | ||||||
|  |         if ((!msg) || ((this.expireDue) && (this.expireDue.getTime() < Date.now()))) { | ||||||
|  |             this.stop(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const chanConv = (<Conversation.ChannelConversation>this.conversation); | ||||||
|  | 
 | ||||||
|  |         await chanConv.sendAd(msg); | ||||||
|  | 
 | ||||||
|  |         // post next ad every 12 - 22 minutes
 | ||||||
|  |         const nextInMs = Math.max(0, (chanConv.nextAd - Date.now())) + | ||||||
|  |             AdManager.POST_DELAY + | ||||||
|  |             Math.random() * AdManager.POST_VARIANCE; | ||||||
|  | 
 | ||||||
|  |         this.adIndex = this.adIndex + 1; | ||||||
|  |         this.nextPostDue = new Date(Date.now() + nextInMs); | ||||||
|  | 
 | ||||||
|  |         this.interval = setTimeout( | ||||||
|  |             async() => { | ||||||
|  |                 await this.sendNextPost(); | ||||||
|  |             }, | ||||||
|  |             nextInMs | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getAds(): string[] { | ||||||
|  |         return this.conversation.settings.adSettings.ads; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getNextAd(): string | undefined { | ||||||
|  |         const ads = this.getAds(); | ||||||
|  | 
 | ||||||
|  |         if (ads.length === 0) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         return ads[this.adIndex % ads.length]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getNextPostDue(): Date | undefined { | ||||||
|  |         return this.nextPostDue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getExpireDue(): Date | undefined { | ||||||
|  |         return this.expireDue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getFirstPost(): Date | undefined { | ||||||
|  |         return this.firstPost; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     start(): void { | ||||||
|  |         const chanConv = (<Conversation.ChannelConversation>this.conversation); | ||||||
|  |         const initialWait = Math.max(Math.random() * AdManager.START_VARIANCE, (chanConv.nextAd - Date.now()) * 1.1); | ||||||
|  | 
 | ||||||
|  |         this.adIndex = 0; | ||||||
|  |         this.active = true; | ||||||
|  |         this.nextPostDue = new Date(Date.now() + initialWait); | ||||||
|  |         this.expireDue = new Date(Date.now() + AdManager.POSTING_PERIOD); | ||||||
|  | 
 | ||||||
|  |         this.interval = setTimeout( | ||||||
|  |             async() => { | ||||||
|  |                 this.firstPost = new Date(); | ||||||
|  | 
 | ||||||
|  |                 await this.sendNextPost(); | ||||||
|  |             }, | ||||||
|  |             initialWait | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     stop(): void { | ||||||
|  |         if (this.interval) | ||||||
|  |             clearTimeout(this.interval); | ||||||
|  | 
 | ||||||
|  |         delete this.interval; | ||||||
|  |         delete this.nextPostDue; | ||||||
|  |         delete this.expireDue; | ||||||
|  |         delete this.firstPost; | ||||||
|  | 
 | ||||||
|  |         this.active = false; | ||||||
|  |         this.adIndex = 0; | ||||||
|  | 
 | ||||||
|  |         // const message = new EventMessage(`Advertisements on channel [channel]${this.conversation.name}[/channel] have expired.`);
 | ||||||
|  |         // addEventMessage(message);
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     renew(): void { | ||||||
|  |         if (!this.active) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         this.expireDue = new Date(Date.now() + 3 * 60 * 60 * 1000); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -50,16 +50,6 @@ export class AdSettings implements Conversation.AdSettings { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export class AdState implements Conversation.AdState { |  | ||||||
|     active = false; |  | ||||||
|     firstPost?: Date = undefined; |  | ||||||
|     nextPostDue?: Date = undefined; |  | ||||||
|     interval?: any = undefined; |  | ||||||
|     adIndex?: number = undefined; |  | ||||||
|     expireDue?: Date = undefined; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| export class ConversationSettings implements Conversation.Settings { | export class ConversationSettings implements Conversation.Settings { | ||||||
|     notify = Conversation.Setting.Default; |     notify = Conversation.Setting.Default; | ||||||
|     highlight = Conversation.Setting.Default; |     highlight = Conversation.Setting.Default; | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import {queuedJoin} from '../fchat/channels'; | import {queuedJoin} from '../fchat/channels'; | ||||||
| import {decodeHTML} from '../fchat/common'; | import {decodeHTML} from '../fchat/common'; | ||||||
| import { AdState, characterImage, ConversationSettings, EventMessage, Message, messageToString } from './common'; | import { AdManager } from './ad-manager'; | ||||||
|  | import { characterImage, ConversationSettings, EventMessage, Message, messageToString } from './common'; | ||||||
| import core from './core'; | import core from './core'; | ||||||
| import {Channel, Character, Conversation as Interfaces} from './interfaces'; | import {Channel, Character, Conversation as Interfaces} from './interfaces'; | ||||||
| import l from './localize'; | import l from './localize'; | ||||||
| @ -30,14 +31,15 @@ abstract class Conversation implements Interfaces.Conversation { | |||||||
|     infoText = ''; |     infoText = ''; | ||||||
|     abstract readonly maxMessageLength: number | undefined; |     abstract readonly maxMessageLength: number | undefined; | ||||||
|     _settings: Interfaces.Settings | undefined; |     _settings: Interfaces.Settings | undefined; | ||||||
|     _adState: Interfaces.AdState | undefined; |  | ||||||
|     protected abstract context: CommandContext; |     protected abstract context: CommandContext; | ||||||
|     protected maxMessages = 50; |     protected maxMessages = 50; | ||||||
|     protected allMessages: Interfaces.Message[] = []; |     protected allMessages: Interfaces.Message[] = []; | ||||||
|     readonly reportMessages: Interfaces.Message[] = []; |     readonly reportMessages: Interfaces.Message[] = []; | ||||||
|     private lastSent = ''; |     private lastSent = ''; | ||||||
|  |     adManager: AdManager; | ||||||
| 
 | 
 | ||||||
|     constructor(readonly key: string, public _isPinned: boolean) { |     constructor(readonly key: string, public _isPinned: boolean) { | ||||||
|  |         this.adManager = new AdManager(this); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     get settings(): Interfaces.Settings { |     get settings(): Interfaces.Settings { | ||||||
| @ -50,17 +52,6 @@ abstract class Conversation implements Interfaces.Conversation { | |||||||
|         state.setSettings(this.key, value); //tslint:disable-line:no-floating-promises
 |         state.setSettings(this.key, value); //tslint:disable-line:no-floating-promises
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     get adState(): Interfaces.AdState { |  | ||||||
|         //tslint:disable-next-line:strict-boolean-expressions
 |  | ||||||
|         return this._adState || (this._adState = state.adStates[this.key] || new AdState()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     set adState(value: Interfaces.AdState) { |  | ||||||
|         this._adState = value; |  | ||||||
|         state.setAdState(this.key, value); //tslint:disable-line:no-floating-promises
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     get isPinned(): boolean { |     get isPinned(): boolean { | ||||||
|         return this._isPinned; |         return this._isPinned; | ||||||
|     } |     } | ||||||
| @ -200,12 +191,11 @@ class PrivateConversation extends Conversation implements Interfaces.PrivateConv | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(this.adState.active) { |         if(this.adManager.isActive()) { | ||||||
|             this.errorText = 'Cannot send ads manually while ad auto-posting is active'; |             this.errorText = 'Cannot send ads manually while ad auto-posting is active'; | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         core.connection.send('PRI', {recipient: this.name, message: this.enteredText}); |         core.connection.send('PRI', {recipient: this.name, message: this.enteredText}); | ||||||
|         const message = createMessage(MessageType.Message, core.characters.ownCharacter, this.enteredText); |         const message = createMessage(MessageType.Message, core.characters.ownCharacter, this.enteredText); | ||||||
|         this.safeAddMessage(message); |         this.safeAddMessage(message); | ||||||
| @ -331,7 +321,7 @@ class ChannelConversation extends Conversation implements Interfaces.ChannelConv | |||||||
|     protected async doSend(): Promise<void> { |     protected async doSend(): Promise<void> { | ||||||
|         const isAd = this.isSendingAds; |         const isAd = this.isSendingAds; | ||||||
| 
 | 
 | ||||||
|         if(this.adState.active) { |         if(this.adManager.isActive()) { | ||||||
|             this.errorText = 'Cannot post ads manually while ad auto-posting is active'; |             this.errorText = 'Cannot post ads manually while ad auto-posting is active'; | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @ -349,7 +339,6 @@ class ChannelConversation extends Conversation implements Interfaces.ChannelConv | |||||||
|         else this.clearText(); |         else this.clearText(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     async sendAd(text: string): Promise<void> { |     async sendAd(text: string): Promise<void> { | ||||||
|         if (text.length < 1) |         if (text.length < 1) | ||||||
|             return; |             return; | ||||||
| @ -399,7 +388,6 @@ class State implements Interfaces.State { | |||||||
|     recentChannels: Interfaces.RecentChannelConversation[] = []; |     recentChannels: Interfaces.RecentChannelConversation[] = []; | ||||||
|     pinned!: {channels: string[], private: string[]}; |     pinned!: {channels: string[], private: string[]}; | ||||||
|     settings!: {[key: string]: Interfaces.Settings}; |     settings!: {[key: string]: Interfaces.Settings}; | ||||||
|     adStates: {[key: string]: Interfaces.AdState} = {}; |  | ||||||
|     modes!: {[key: string]: Channel.Mode | undefined}; |     modes!: {[key: string]: Channel.Mode | undefined}; | ||||||
|     windowFocused = document.hasFocus(); |     windowFocused = document.hasFocus(); | ||||||
| 
 | 
 | ||||||
| @ -444,12 +432,6 @@ class State implements Interfaces.State { | |||||||
|         await core.settingsStore.set('conversationSettings', this.settings); |         await core.settingsStore.set('conversationSettings', this.settings); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     setAdState(key: string, value: Interfaces.AdState): void { |  | ||||||
|         this.adStates[key] = value; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     show(conversation: Conversation): void { |     show(conversation: Conversation): void { | ||||||
|         this.selectedConversation.onHide(); |         this.selectedConversation.onHide(); | ||||||
|         conversation.unread = Interfaces.UnreadState.None; |         conversation.unread = Interfaces.UnreadState.None; | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| import {Connection} from '../fchat'; | import {Connection} from '../fchat'; | ||||||
| 
 | 
 | ||||||
| import {Channel, Character} from '../fchat/interfaces'; | import {Channel, Character} from '../fchat/interfaces'; | ||||||
|  | import { AdManager } from './ad-manager'; | ||||||
| export {Connection, Channel, Character} from '../fchat/interfaces'; | export {Connection, Channel, Character} from '../fchat/interfaces'; | ||||||
| export const userStatuses: ReadonlyArray<Character.Status> = ['online', 'looking', 'away', 'busy', 'dnd']; | export const userStatuses: ReadonlyArray<Character.Status> = ['online', 'looking', 'away', 'busy', 'dnd']; | ||||||
| export const channelModes: ReadonlyArray<Channel.Mode> = ['chat', 'ads', 'both']; | export const channelModes: ReadonlyArray<Channel.Mode> = ['chat', 'ads', 'both']; | ||||||
| @ -98,22 +99,10 @@ export namespace Conversation { | |||||||
|         readonly adSettings: AdSettings; |         readonly adSettings: AdSettings; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     export interface AdSettings { |     export interface AdSettings { | ||||||
|         readonly ads: string[]; |         readonly ads: string[]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     export interface AdState { |  | ||||||
|         active: boolean; |  | ||||||
|         firstPost?: Date; |  | ||||||
|         nextPostDue?: Date; |  | ||||||
|         expireDue?: Date; |  | ||||||
|         interval?: any; |  | ||||||
|         adIndex?: number; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     export const enum UnreadState { None, Unread, Mention } |     export const enum UnreadState { None, Unread, Mention } | ||||||
| 
 | 
 | ||||||
|     export interface Conversation { |     export interface Conversation { | ||||||
| @ -127,7 +116,7 @@ export namespace Conversation { | |||||||
|         readonly key: string |         readonly key: string | ||||||
|         readonly unread: UnreadState |         readonly unread: UnreadState | ||||||
|         settings: Settings |         settings: Settings | ||||||
|         adState: AdState |         readonly adManager: AdManager; | ||||||
|         send(): Promise<void> |         send(): Promise<void> | ||||||
|         clear(): void |         clear(): void | ||||||
|         loadLastSent(): void |         loadLastSent(): void | ||||||
|  | |||||||
| @ -17,6 +17,14 @@ const strings: {[key: string]: string | undefined} = { | |||||||
|     'action.updateAvailable': 'UPDATE AVAILABLE', |     'action.updateAvailable': 'UPDATE AVAILABLE', | ||||||
|     'action.update': 'Restart now!', |     'action.update': 'Restart now!', | ||||||
|     'action.cancel': 'Cancel', |     'action.cancel': 'Cancel', | ||||||
|  |     'admgr.postingBegins': 'Posting beings in {0}m {1}s', | ||||||
|  |     'admgr.nextPostDue': 'Next ad in {0}m {1}s', | ||||||
|  |     'admgr.expiresIn': ', auto-posting expires in {0}m {1}s', | ||||||
|  |     'admgr.noAds': 'No ads have been set up -- auto-posting will be cancelled.', | ||||||
|  |     'admgr.activeHeader': 'Auto-Posting Ads', | ||||||
|  |     'admgr.comingNext': 'Coming Next', | ||||||
|  |     'admgr.renew': 'Renew', | ||||||
|  |     'admgr.toggleAutoPost': 'Auto-Post Ads', | ||||||
|     'consoleWarning.head': 'THIS IS THE DANGER ZONE.', |     'consoleWarning.head': 'THIS IS THE DANGER ZONE.', | ||||||
|     'consoleWarning.body': `ANYTHING YOU WRITE OR PASTE IN HERE COULD BE USED TO STEAL YOUR PASSWORDS OR TAKE OVER YOUR ENTIRE COMPUTER. This is where happiness goes to die. If you aren't a developer or a special kind of daredevil, please get out of here!`, |     'consoleWarning.body': `ANYTHING YOU WRITE OR PASTE IN HERE COULD BE USED TO STEAL YOUR PASSWORDS OR TAKE OVER YOUR ENTIRE COMPUTER. This is where happiness goes to die. If you aren't a developer or a special kind of daredevil, please get out of here!`, | ||||||
|     'help': 'Help', |     'help': 'Help', | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | { | ||||||
|  |   "compilerOptions": { | ||||||
|  |     "target": "es2017", | ||||||
|  |     "module": "commonjs", | ||||||
|  |     "sourceMap": true, | ||||||
|  |     "allowJs": true, | ||||||
|  |     "noEmitHelpers": true, | ||||||
|  |     "importHelpers": true, | ||||||
|  |     "forceConsistentCasingInFileNames": true, | ||||||
|  |     "strict": true, | ||||||
|  |     "noUnusedLocals": true, | ||||||
|  |     "noUnusedParameters": true | ||||||
|  |   }, | ||||||
|  |   "include": ["./electron/main.ts"] | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user