πΎ Sessions
What are sessions for?β
Because, bot only has access to the information of the currently incoming update (e.g. message), i.e. the
information that is available on the context object ctx
.
Consequently, if you do want to access old data, you have to store it as soon as it arrives. This means that you must
have a data storage, such as a file, a database, or an in-memory storage.
Sessions are a simple key β value storage for data that is needed temporarily and their loss, etc., will not affect the bot's work in any way.
Sessions provide a simple interface for storing and retrieving data in handlers when it needs to be received and stored in handlers over a time interval greater than 1 update lifecycle.
In other words, sessions are used to store information about the current dialogue between the user and the bot. This allows the bot to remember the state of the conversation between requests, and respond to user requests contextually.
For example:
- Intermediate handler's data on a time interval greater than the update lifecycle
- User input data (using scenes / etc.)
- Other things like storing user locale (for i18n)
Use caseβ
It is a very common thing for bots to store some piece of data per chat. For example, let's say we want to build a bot that counts the number of received times that a message in current chat for every user. When our bot receives a message, it has to remember how many times it saw the message in that chat before. Sessions are a way to store data per chat.
How session worksβ
By default, sessions tied to chat identifier and user identifier from context (ctx
) and store data as object where
key is ctx.from.id
+ ctx.chat.id
with :
delimiter and value - your session data.
Example of stored object:
{
"user_id:chat_id": { "someProp": "my data" }
"user_id1:chat_id1": { "someProp": "other data" }
...
}
Flowchart of sessions:β
Click to expand
Configurationβ
Accepted fields:
Property | Description | Required | Default value |
---|---|---|---|
getSessionKey | Session key generator function, accepts context, returns key string or null | No | (ctx) => ctx.from && ctx.chat && ctx.from.id + ':' + ctx.chat.id |
ttl | Time-to-live in seconds | No | - |
property | Session property name for context | No | session |
store | Map compatible storage for storing session data. Can be handwritten storage / keyv / native Map | No | new Map() |
Availbale getters / settersβ
Property | Description | Type |
---|---|---|
store | Returns current store | Getter |
ttl | Returns current TTL | Getter |
ttl | Sets a new TTL for sessions | Setter |
Custom session key generatorβ
You can write own key generator and pass into session middleware factory function.
For example, default session key generator doesn't support inline query, because inline queries doesn't have
ctx.chat
property, this means not accessible in inline queries by default, let's fix that for inline queries
in bot direct messages (when chat type = sender)
bot.use(
session({
getSessionKey: (ctx) => {
// This same as in default session key generator
if (ctx.from?.id && ctx.chat?.id) {
return `${ctx.from.id}:${ctx.chat.id}`
}
// If inline mode called from bot direct messages, use same session key as used in "private"
if (ctx.inlineQuery?.chat_type === 'sender') {
return `${ctx.from.id}:${ctx.from.id}`
}
return null // Return null for other cases (for other - session not available)
}
})
)
Other examples of session storage for chat id and user id
bot.use(
session({
// Stores data per user.
getSessionKey: (ctx) => {
if (ctx.from?.id) {
return `${ctx.from.id}`
}
return null // Return null for other cases (for other - session not available)
}
})
)
bot.use(
session({
// Stores data per chat
getSessionKey: (ctx) => {
if (ctx.chat?.id) {
return `${ctx.chat.id}`
}
return null // Return null for other cases (for other - session not available)
}
})
)
Changing session property nameβ
If you want to use a session with Stage
, you need to specify a new name of the session property in its settings, as the default is ctx.session
bot.use(
session({
property: 'mySessionProp'
})
)
bot.on('message', ctx => {
ctx.mySessionProp // Session available in mySessionProp
})
Session Time-to-live (TTL) / Timeoutsβ
You can define TTL for session. With the TTL set, the content of the session will be destroyed after it expires
When the session expires, data about the current scene and so on will also be deleted (the user will no longer be in the scene)
bot.use(
session({
ttl: 60 // 60 seconds
})
)
Multi sessionsβ
You can create separated sessions with different session keys, just create two session middlewares with different key generator and/or property names:
// Default session for chat id + user id
bot.use(session())
// Session stored per chat, same for all chat users
bot.use(
session({
property: 'chatSession',
// Stores data per chat
getSessionKey: (ctx) => {
if (ctx.chat?.id) {
return `${ctx.chat.id}`
}
return null // Return null for other cases (for other - session not available)
}
})
)
bot.on('message', ctx => {
ctx.session // Default session for chat id + user id
ctx.chatSession // Session per chat
})
Write your own storeβ
You can write your own store, just make it compatible with the
Map API.
At the moment, at least the implementation of these methods is required -
get
,
set
.
Methods can be either asynchronous (returning a
Promise
) or synchronous
Use with external storage (with keyv)β
- MongoDB
- SQLite
- MySQL
- Redis
- PostgreSQL
- Etcd
- Memcache
- File system
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv @keyv/mongo
yarn add keyv @keyv/mongo
pnpm add keyv @keyv/mongo
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const bot = new Opengram(process.env.BOT_TOKEN)
const sessionsStore = new Keyv('mongodb://user:pass@localhost:27017/dbname', { namespace: 'sessions' });
// Handle connection errors
sessionsStore.on('error', err => console.log('Connection Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv @keyv/sqlite
yarn add keyv @keyv/sqlite
pnpm add keyv @keyv/sqlite
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const bot = new Opengram(process.env.BOT_TOKEN)
const sessionsStore = new Keyv('sqlite://path/to/database.sqlite', { namespace: 'sessions' });
// Handle connection errors
sessionsStore.on('error', err => console.log('Connection Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv @keyv/mysql
yarn add keyv @keyv/mysql
pnpm add keyv @keyv/mysql
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const bot = new Opengram(process.env.BOT_TOKEN)
const sessionsStore = new Keyv('mysql://user:pass@localhost:3306/dbname', { namespace: 'sessions' });
// Handle connection errors
sessionsStore.on('error', err => console.log('Connection Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv @keyv/redis
yarn add keyv @keyv/redis
pnpm add keyv @keyv/redis
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const bot = new Opengram(process.env.BOT_TOKEN)
const sessionsStore = new Keyv('redis://user:pass@localhost:6379', { namespace: 'sessions' });
// Handle connection errors
sessionsStore.on('error', err => console.log('Connection Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv @keyv/postgres
yarn add keyv @keyv/postgres
pnpm add keyv @keyv/postgres
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const bot = new Opengram(process.env.BOT_TOKEN)
const sessionsStore = new Keyv('postgresql://user:pass@localhost:5432/dbname', { namespace: 'sessions' });
// Handle connection errors
sessionsStore.on('error', err => console.log('Connection Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv @keyv/etcd
yarn add keyv @keyv/etcd
pnpm add keyv @keyv/etcd
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const bot = new Opengram(process.env.BOT_TOKEN)
const sessionsStore = new Keyv('etcd://localhost:2379', { namespace: 'sessions' });
// Handle connection errors
sessionsStore.on('error', err => console.log('Connection Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv @keyv/memcache
yarn add keyv @keyv/memcache
pnpm add keyv @keyv/memcache
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const KeyvMemcache = require('@keyv/memcache');
const bot = new Opengram(process.env.BOT_TOKEN)
const memcache = new KeyvMemcache('user:pass@localhost:11211');
const sessionsStore = new Keyv({ store: memcache });
// Handle connection errors
sessionsStore.on('error', err => console.log('Connection Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Before use, install the following dependencies:
- NPM
- Yarn
- pnpm
npm install keyv keyv-file
yarn add keyv keyv-file
pnpm add keyv keyv-file
const { Opengram, session } = require('opengram')
const Keyv = require('keyv');
const { KeyvFile } = require('keyv-file')
const bot = new Opengram(process.env.BOT_TOKEN)
const fileStore = new KeyvFile({
filename: `./sessions.json`,
writeDelay: 100
})
const sessionsStore = new Keyv({ store: fileStore });
// Handle errors
sessionsStore.on('error', err => console.log('Error', err));
bot.use(
session({
store: sessionsStore
})
)
// ...
Additional Informationβ
- Source: https://github.com/OpengramJS/opengram/blob/master/src/session.js
- Keyv: https://github.com/jaredwray/keyv/tree/main/packages/keyv
- Memcache adapter: https://github.com/jaredwray/keyv/tree/main/packages/memcache
- Mongo adapter: https://github.com/jaredwray/keyv/tree/main/packages/mongo
- Mysql adapter: https://github.com/jaredwray/keyv/tree/main/packages/mysql
- Postgres adapter: https://github.com/jaredwray/keyv/tree/main/packages/postgres
- Redis adapter: https://github.com/jaredwray/keyv/tree/main/packages/redis
- Sqlite adapter: https://github.com/jaredwray/keyv/tree/main/packages/sqlite
- Keyv-file adapter: https://github.com/zaaack/keyv-file