twitch-emote
Version:
splice twitch emotes from a message string
123 lines (96 loc) • 3.13 kB
text/typescript
import { access, mkdir } from 'fs/promises'
import { join } from 'path'
import { readJson, writeJson } from './fs_json'
import { getSetting } from './settings'
import { ChannelIdentifier, EmoteData } from './types'
import { isChannelThrow, uniqueArr } from './util'
const Dir = (fname?: string) => join(getSetting('cacheDir'), fname || '')
export interface FsResponse<T> {
data: T
timestamp: number
}
export async function ensureCacheFolder() {
try {
await access(Dir())
} catch (e) {
await mkdir(Dir())
}
}
export async function loadCacheRaw<T = {}>(fileName: string): Promise<T | null> {
await ensureCacheFolder()
try {
await access(Dir(fileName))
} catch (e) {
return null
}
return await readJson(Dir(fileName))
}
export async function loadCache<T = []>(fileName: string, def: T): Promise<FsResponse<T>> {
let file = await loadCacheRaw<FsResponse<T>>(fileName)
if (!file) {
return { data: def, timestamp: 0 }
}
return file
}
async function writeCache(fileName: string, data: any) {
await ensureCacheFolder()
await writeJson(Dir(fileName), { data, timestamp: Date.now() })
}
export async function loadChannelList() {
const data = await loadCache<string[]>(`channels`, [])
return data.data
}
export async function saveChannelList(channels: string[]) {
return await writeCache('channels', uniqueArr(channels))
}
export async function loadGlobalCache(): Promise<FsResponse<EmoteData[]>> {
return loadCache(`global`, [])
}
export async function loadChannelCache(channel: string): Promise<FsResponse<EmoteData[]>> {
isChannelThrow(channel)
return loadCache(`channel.${channel}`, [])
}
export async function loadIdentifierCache(
channel: string
): Promise<FsResponse<ChannelIdentifier | null>> {
return loadCache(`identifier.${channel}`, null)
}
export async function saveGlobalCache(cache: EmoteData[]) {
await writeCache(`global`, cache)
}
export async function saveChannelCache(
channel: string,
emotes: EmoteData[],
identifier: ChannelIdentifier
) {
isChannelThrow(channel)
await writeCache(`channel.${channel}`, emotes)
await writeCache(`identifier.${channel}`, identifier)
let channels = await loadChannelList()
if (!channels.includes(channel)) {
channels.push(channel)
await saveChannelList(channels)
}
}
export interface AllChannelData {
emotes: FsResponse<EmoteData[]>
identifier: FsResponse<ChannelIdentifier | null>
}
export interface AllChannelDataCollection {
[channel: string]: AllChannelData
}
export async function loadChannels(): Promise<AllChannelDataCollection | null> {
const channels = await loadChannelList()
let foundData: AllChannelDataCollection = {}
if (!channels.length) {
return null
} else {
for (const chan of uniqueArr(channels)) {
foundData[chan] = {
emotes: await loadChannelCache(chan),
identifier: await loadIdentifierCache(chan),
}
}
return foundData
}
}