ts3bot/ts3settings.iced

233 lines
6.4 KiB
CoffeeScript

sqlite3 = require("sqlite3") #.verbose()
SQLite3Database = sqlite3.Database
path = require "path"
mkdirp = require "mkdirp"
SimpleIni = require "simple-ini"
fs = require "fs"
merge = require "merge"
getLogger = require "./logger"
# some properties sugar from http://bl.ocks.org/joyrexus/65cb3780a24ecd50f6df
Function::getter = (prop, get) ->
Object.defineProperty @prototype, prop, {get, configurable: yes}
Function::setter = (prop, set) ->
Object.defineProperty @prototype, prop, {set, configurable: yes}
module.exports = class SettingsFile
db: null
identities: null
defaultIdentity: null
constructor: (@configPath) ->
@log = getLogger "TS3Settings"
try
mkdirp.sync @configPath
catch err
throw new Error "Could not create TS3 config directory."
@getter "isInitialized", -> () => fs.existsSync(path.join(@configPath, "settings.db")) and fs.existsSync(path.join(@configPath, "ts3clientui_qt.secrets.conf"))
@getter "isReady", -> () => @db != null
open: (cb) =>
# settings database
@db = new SQLite3Database path.join(@configPath, "settings.db")
await @db.serialize defer()
await @query "CREATE TABLE IF NOT EXISTS TS3Tables (key varchar NOT NULL UNIQUE,timestamp integer unsigned NOT NULL)", defer()
# secrets file
@identities = []
@defaultIdentity = null
secretsPath = path.join(@configPath, "ts3clientui_qt.secrets.conf")
if fs.existsSync(secretsPath)
secrets = new SimpleIni (() => fs.readFileSync(secretsPath, "utf-8")),
quotedValues: false
for i in [1 .. secrets.Identities.size]
@identities.push
id: secrets.Identities["#{i}/id"]
identity: secrets.Identities["#{i}/identity"]
nickname: secrets.Identities["#{i}/nickname"]
@defaultIdentity = secrets.Identities.SelectedIdentity
cb?()
close: (cb) =>
if not @isReady
@log.warn "Tried to close TS3 settings when already closed"
return
await @db.close defer()
# Build secrets INI structure
secrets = new SimpleIni null,
quotedValues: false
secrets.General = {}
secrets.Bookmarks =
size: 0
secrets.Identities =
size: @identities.length
index = 1
for identity in @identities
for key, value of identity
secrets.Identities["#{index}/#{key}"] = value
index++
if @defaultIdentity
secrets.Identities.SelectedIdentity = @defaultIdentity
# Generate INI content
await secrets.save defer(iniText)
fs.writeFileSync path.join(@configPath, "ts3clientui_qt.secrets.conf"), iniText
@identities = null
@defaultIdentity = null
@db = null
cb?()
setMultiple: (sets, cb) =>
for set in sets
await @set set[0], set[1], set[2], defer(err)
if err
throw err
cb?()
set: (table, key, value, cb) =>
if not @isReady
throw new Error "You need to run open on this instance of TS3Settings first"
return
if not table
throw new Error "Need table"
await @query "create table if not exists #{table} (timestamp integer unsigned NOT NULL, key varchar NOT NULL UNIQUE, value varchar)", defer()
if not key
return
if not (typeof value == "string" || value instanceof String)
# serialize from object to ts3 dict text
strval = ""
for own k of value
strval += k + "=" + value[k] + "\n"
value = strval
timestamp = Math.round (new Date).getTime() / 1000
stmt = @db.prepare "insert or replace into TS3Tables (key, timestamp) values (?, ?)"
stmt.run table, timestamp
await stmt.finalize defer()
stmt = @db.prepare "insert or replace into #{table} (timestamp, key, value) values (?, ?, ?)"
stmt.run timestamp, key, value
await stmt.finalize defer()
cb?()
query: (stmt, cb) =>
if not @isReady
throw new Error "You need to run open on this instance of TS3Settings first"
return
await @db.run stmt, defer()
cb?()
importIdentity: (identityFilePath, cb) =>
if not @isReady
throw new Error "You need to run open on this instance of TS3Settings first"
return
if not identityFilePath
throw new Error "Need identity file path"
@log.info "Importing identity from #{identityFilePath}..."
# open identity file
idFile = new SimpleIni (() => fs.readFileSync(identityFilePath, "utf-8")),
quotedValues: true
importedIdentity = {}
for own k, v of idFile.Identity
importedIdentity[k] = v
for identity in @identities
if identity.id == importedIdentity.id
throw new Error "Identity with same ID already exists"
@identities.push importedIdentity
@log.info "Identity #{importedIdentity.id} imported successfully!"
cb? @constructIdentityObject importedIdentity
importIdentitySync: (identityFilePath) =>
await @importIdentity identityFilePath, defer retval
return retval
getIdentities: (cb) =>
if not @isReady
throw new Error "You need to run open on this instance of TS3Settings first"
return
identities = []
for identity in @identities
identities.push @constructIdentityObject identity
cb? identities
getIdentitiesSync: () =>
await @getIdentities defer retval
return retval
getIdentitiesSize: () =>
if not @isReady
throw new Error "You need to run open on this instance of TS3Settings first"
return
@identities.length
getSelectedIdentity: () =>
if not @isReady
throw new Error "You need to run open on this instance of TS3Settings first"
return
if not @defaultIdentity
return null
for own index, identity of @identities
if identity.id == @defaultIdentity
return @constructIdentityObject identity
clearIdentities: () =>
if not @isReady
throw new Error "You need to run open on this instance of TS3Settings first"
return
@log.debug "Clearing all identities"
@identities.length = 0
return
constructIdentityObject: (id) =>
settingsObj = @
clonedId = merge(true, id)
return merge clonedId, # true causes object to be cloned
select: () ->
settingsObj.defaultIdentity = @id
update: () ->
settingsObj.log.silly "Requested update of #{id.id}"
for own index, identity of settingsObj.identities
if identity.id == id.id
# remove functions from this object
cleanIdentity = merge @
for own k, v of cleanIdentity
if typeof v == "function"
delete cleanIdentity[k]
# now this is our new identity object!
settingsObj.log.silly "Updating identity #{id.id}"
settingsObj.identities[index] = cleanIdentity
return
remove: () ->
for own index, identity of settingsObj.identities
if identity.id == id.id
delete settingsObj.identities[index]
break
# TODO: Select another identity as default