-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
111 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,26 @@ | ||
'use strict' | ||
|
||
const storage = require('./storage') | ||
|
||
const invisibleSpace = '\u2063' | ||
|
||
const command = async (ctx, next) => { | ||
const commandMiddleware = async (ctx, next) => { | ||
if (!ctx.message) return next() | ||
if (!ctx.session) return Promise.reject('ctx.session is missing') | ||
|
||
const msg = ctx.message | ||
const prevCmd = await storage.getCommand(msg.chat.id) | ||
ctx.state.prevCmd = prevCmd | ||
ctx.prevCommand = await ctx.session.get('cmd') | ||
|
||
if (!Array.isArray(msg.entities)) return next() | ||
const entity = msg.entities.find(e => e.type === 'bot_command' && e.offset === 0) | ||
if (!entity) return next() | ||
const cmd = msg.text.substr(entity.offset, entity.length).slice(1).toLowerCase() | ||
ctx.state.cmd = cmd | ||
ctx.command = cmd | ||
|
||
const argsStart = entity.offset + entity.length + 1 | ||
if (msg.text[argsStart - 1] === invisibleSpace) ctx.state.args = [] | ||
else ctx.state.args = msg.text.slice(argsStart).split(/\s+/) | ||
if (msg.text[argsStart - 1] === invisibleSpace) ctx.args = [] | ||
else ctx.args = msg.text.slice(argsStart).split(/\s+/).filter(arg => !!arg) | ||
|
||
if (prevCmd !== cmd) await storage.putCommand(msg.chat.id, cmd) | ||
if (ctx.prevCommand !== cmd) await ctx.session.put('cmd', cmd) | ||
next() | ||
} | ||
|
||
module.exports = command | ||
module.exports = commandMiddleware |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
'use strict' | ||
|
||
const level = require('level') | ||
|
||
const last = '\xff' | ||
|
||
const createSessionMiddleware = (pathToDb) => { | ||
const db = level(pathToDb, {valueEncoding: 'json'}) | ||
|
||
const _get = (key) => { | ||
return db.get(key) | ||
.catch((err) => { | ||
if (err.notFound) return null | ||
else throw err | ||
}) | ||
} | ||
|
||
const createSession = (chat) => { | ||
const prefix = chat + ':' | ||
const get = key => _get(prefix + key) | ||
const put = (key, val) => db.put(prefix + key, val) | ||
|
||
const session = {get, put} | ||
Object.defineProperty(session, 'prefix', {value: prefix}) | ||
Object.defineProperty(session, 'db', {value: db}) | ||
return session | ||
} | ||
|
||
const sessionMiddleware = (ctx, next) => { | ||
Object.defineProperty(ctx, 'session', { | ||
value: createSession(ctx.chat.id), | ||
enumerable: true | ||
}) | ||
next() | ||
} | ||
return sessionMiddleware | ||
} | ||
|
||
module.exports = createSessionMiddleware |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,60 @@ | ||
'use strict' | ||
|
||
const level = require('level') | ||
const path = require('path') | ||
const hifo = require('hifo') | ||
|
||
const last = '\xff' | ||
|
||
const db = level(path.join(__dirname, '..', 'vbb-telegram.ldb'), { | ||
valueEncoding: 'json' | ||
}) | ||
|
||
const get = (key) => { | ||
return db.get(key) | ||
.catch((err) => { | ||
if (err.notFound) return null | ||
else throw err | ||
}) | ||
} | ||
|
||
const getCommand = user => get(user + ':cmd') | ||
|
||
const putCommand = (user, cmd) => { | ||
return db.put(user + ':cmd', cmd) | ||
} | ||
|
||
const createGetData = (user, cmd) => { | ||
const getData = key => { | ||
console.error('get', user + ':data:' + cmd + ':' + key) | ||
return get(user + ':data:' + cmd + ':' + key) | ||
} | ||
return getData | ||
} | ||
|
||
const createPutData = (user, cmd) => { | ||
const putData = (key, val) => { | ||
console.error('put', user + ':data:' + cmd + ':' + key, val) | ||
return db.put(user + ':data:' + cmd + ':' + key, val) | ||
} | ||
return putData | ||
} | ||
|
||
// todo: sth more efficient | ||
const getTopLocations = (user) => { | ||
const ns = user + ':locations:' | ||
const top = hifo(hifo.highest('count'), 3) | ||
|
||
const onItem = ({key, value}) => { | ||
const id = key.slice(ns.length) | ||
top.add({id, count: value}) | ||
} | ||
|
||
const items = db.createReadStream({gt: ns, lt: ns + last}) | ||
const range = (db, prefix, onItem) => { | ||
const items = db.createReadStream({gt: prefix, lt: prefix + last}) | ||
return new Promise((resolve, reject) => { | ||
items.once('error', (err) => { | ||
reject(err) | ||
items.destroy() | ||
}) | ||
items.once('end', () => { | ||
resolve(top.data.map(res => res.id)) | ||
}) | ||
items.once('end', () => resolve()) | ||
items.on('data', onItem) | ||
}) | ||
} | ||
|
||
const incLocation = async (user, id) => { | ||
const key = user + ':locations:' + id | ||
try { | ||
const count = await db.get(key) | ||
await db.put(key, count + 1) | ||
} catch (err) { | ||
if (err.notFound) await db.put(key, 1) | ||
else throw err | ||
const storageMiddleware = (ctx, next) => { | ||
if (!ctx.session) return Promise.reject('ctx.session is missing') | ||
|
||
const createGetData = (cmd) => { | ||
const getData = key => ctx.session.get('data:' + cmd + ':' + key) | ||
return getData | ||
} | ||
const createPutData = (cmd) => { | ||
const putData = (key, val) => ctx.session.put('data:' + cmd + ':' + key, val) | ||
return putData | ||
} | ||
} | ||
|
||
module.exports = { | ||
getCommand, putCommand, | ||
createGetData, createPutData, | ||
getTopLocations, incLocation | ||
// todo: use sth more efficient | ||
const getTopLocations = async () => { | ||
const prefix = ctx.session.prefix + 'locs:' | ||
const top = hifo(hifo.highest('count'), 3) | ||
|
||
const onItem = ({key, value}) => { | ||
const id = key.slice(prefix.length) | ||
top.add({id, count: value}) | ||
} | ||
await range(ctx.session.db, prefix, onItem) | ||
return top.data.map(res => res.id) | ||
} | ||
const incLocation = async (id) => { | ||
const key = 'locs:' + id | ||
const count = await ctx.session.get(key) | ||
if (!count) await ctx.session.put(key, 1) | ||
else await ctx.session.put(key, count + 1) | ||
} | ||
|
||
const storage = { | ||
createGetData, createPutData, | ||
getTopLocations, incLocation | ||
} | ||
Object.defineProperty(ctx, 'storage', { | ||
value: storage, | ||
enumerable: true | ||
}) | ||
next() | ||
} | ||
|
||
module.exports = storageMiddleware |