├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── index.ts └── keys.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MongoDB session middleware for Telegraf 2 | 3 | MongoDB powered simple session middleware for [Telegraf 4.0](https://github.com/telegraf/telegraf) with TypeScript support. 4 | 5 | ## Installation 6 | 7 | ```js 8 | $ npm install telegraf-session-mongodb 9 | ``` 10 | 11 | ```js 12 | $ yarn add telegraf-session-mongodb 13 | ``` 14 | 15 | ## Example 16 | 17 | [Full JavaScript example can be found here.](https://github.com/alexnzarov/telegraf-session-mongodb-examples/tree/master/javascript-example) 18 | 19 | ```js 20 | const { Telegraf } = require('telegraf'); 21 | const { MongoClient } = require('mongodb'); 22 | const { session } = require('telegraf-session-mongodb'); 23 | 24 | const bot = new Telegraf(process.env.BOT_TOKEN); 25 | 26 | MongoClient.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }) 27 | .then(client => { 28 | const db = client.db(); 29 | bot.use(session(db, { collectionName: 'sessions' })); 30 | }); 31 | ``` 32 | 33 | ## Example (TypeScript) 34 | 35 | [Full TypeScript example can be found here.](https://github.com/alexnzarov/telegraf-session-mongodb-examples/tree/master/typescript-example) 36 | 37 | ```ts 38 | import { Context, Telegraf } from 'telegraf'; 39 | import { MongoClient } from 'mongodb'; 40 | import { session } from 'telegraf-session-mongodb'; 41 | 42 | export interface SessionContext extends Context { 43 | session: any; 44 | }; 45 | 46 | const bot = new Telegraf(process.env.BOT_TOKEN); 47 | 48 | MongoClient.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }) 49 | .then(client => { 50 | const db = client.db(); 51 | bot.use(session(db, { sessionName: 'session', collectionName: 'sessions' })); 52 | }); 53 | ``` 54 | 55 | ## API 56 | 57 | ### Options 58 | 59 | * `collectionName`: name for MongoDB collection (default: `sessions`) 60 | * `sessionName`: context property name (default: `session`) 61 | * `sessionKeyFn`: function that generates the session key from the context ([default implementation](https://github.com/alexnzarov/telegraf-session-mongodb/blob/master/src/keys.ts#L10-L16), [legacy deprecated function](https://github.com/alexnzarov/telegraf-session-mongodb/blob/master/src/keys.ts#L21-L31)) 62 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegraf-session-mongodb", 3 | "version": "2.0.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/bson": { 8 | "version": "4.0.3", 9 | "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", 10 | "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", 11 | "dev": true, 12 | "requires": { 13 | "@types/node": "*" 14 | } 15 | }, 16 | "@types/mongodb": { 17 | "version": "3.6.8", 18 | "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.8.tgz", 19 | "integrity": "sha512-8qNbL5/GFrljXc/QijcuQcUMYZ1iWNcqnJ6tneROwbfU0LsAjQ9bmq3aHi5lWXM4cyBPd2F/n9INAk/pZZttHw==", 20 | "dev": true, 21 | "requires": { 22 | "@types/bson": "*", 23 | "@types/node": "*" 24 | } 25 | }, 26 | "@types/node": { 27 | "version": "14.14.31", 28 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz", 29 | "integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==", 30 | "dev": true 31 | }, 32 | "abort-controller": { 33 | "version": "3.0.0", 34 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 35 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 36 | "dev": true, 37 | "requires": { 38 | "event-target-shim": "^5.0.0" 39 | } 40 | }, 41 | "bl": { 42 | "version": "2.2.1", 43 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", 44 | "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", 45 | "requires": { 46 | "readable-stream": "^2.3.5", 47 | "safe-buffer": "^5.1.1" 48 | } 49 | }, 50 | "bson": { 51 | "version": "1.1.5", 52 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", 53 | "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" 54 | }, 55 | "core-util-is": { 56 | "version": "1.0.2", 57 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 58 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 59 | }, 60 | "debug": { 61 | "version": "4.3.1", 62 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 63 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 64 | "dev": true, 65 | "requires": { 66 | "ms": "2.1.2" 67 | } 68 | }, 69 | "denque": { 70 | "version": "1.5.0", 71 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", 72 | "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" 73 | }, 74 | "event-target-shim": { 75 | "version": "5.0.1", 76 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 77 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 78 | "dev": true 79 | }, 80 | "inherits": { 81 | "version": "2.0.4", 82 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 83 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 84 | }, 85 | "isarray": { 86 | "version": "1.0.0", 87 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 88 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 89 | }, 90 | "memory-pager": { 91 | "version": "1.5.0", 92 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 93 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 94 | "optional": true 95 | }, 96 | "minimist": { 97 | "version": "1.2.5", 98 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 99 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 100 | "dev": true 101 | }, 102 | "module-alias": { 103 | "version": "2.2.2", 104 | "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", 105 | "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==", 106 | "dev": true 107 | }, 108 | "mongodb": { 109 | "version": "3.6.4", 110 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.4.tgz", 111 | "integrity": "sha512-Y+Ki9iXE9jI+n9bVtbTOOdK0B95d6wVGSucwtBkvQ+HIvVdTCfpVRp01FDC24uhC/Q2WXQ8Lpq3/zwtB5Op9Qw==", 112 | "requires": { 113 | "bl": "^2.2.1", 114 | "bson": "^1.1.4", 115 | "denque": "^1.4.1", 116 | "require_optional": "^1.0.1", 117 | "safe-buffer": "^5.1.2", 118 | "saslprep": "^1.0.0" 119 | } 120 | }, 121 | "ms": { 122 | "version": "2.1.2", 123 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 124 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 125 | "dev": true 126 | }, 127 | "node-fetch": { 128 | "version": "2.6.1", 129 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 130 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", 131 | "dev": true 132 | }, 133 | "p-timeout": { 134 | "version": "4.1.0", 135 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", 136 | "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==", 137 | "dev": true 138 | }, 139 | "process-nextick-args": { 140 | "version": "2.0.1", 141 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 142 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 143 | }, 144 | "readable-stream": { 145 | "version": "2.3.7", 146 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 147 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 148 | "requires": { 149 | "core-util-is": "~1.0.0", 150 | "inherits": "~2.0.3", 151 | "isarray": "~1.0.0", 152 | "process-nextick-args": "~2.0.0", 153 | "safe-buffer": "~5.1.1", 154 | "string_decoder": "~1.1.1", 155 | "util-deprecate": "~1.0.1" 156 | }, 157 | "dependencies": { 158 | "safe-buffer": { 159 | "version": "5.1.2", 160 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 161 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 162 | } 163 | } 164 | }, 165 | "require_optional": { 166 | "version": "1.0.1", 167 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 168 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 169 | "requires": { 170 | "resolve-from": "^2.0.0", 171 | "semver": "^5.1.0" 172 | } 173 | }, 174 | "resolve-from": { 175 | "version": "2.0.0", 176 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 177 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 178 | }, 179 | "safe-buffer": { 180 | "version": "5.2.1", 181 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 182 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 183 | }, 184 | "sandwich-stream": { 185 | "version": "2.0.2", 186 | "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", 187 | "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==", 188 | "dev": true 189 | }, 190 | "saslprep": { 191 | "version": "1.0.3", 192 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 193 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 194 | "optional": true, 195 | "requires": { 196 | "sparse-bitfield": "^3.0.3" 197 | } 198 | }, 199 | "semver": { 200 | "version": "5.7.1", 201 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 202 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 203 | }, 204 | "sparse-bitfield": { 205 | "version": "3.0.3", 206 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 207 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 208 | "optional": true, 209 | "requires": { 210 | "memory-pager": "^1.0.2" 211 | } 212 | }, 213 | "string_decoder": { 214 | "version": "1.1.1", 215 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 216 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 217 | "requires": { 218 | "safe-buffer": "~5.1.0" 219 | }, 220 | "dependencies": { 221 | "safe-buffer": { 222 | "version": "5.1.2", 223 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 224 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 225 | } 226 | } 227 | }, 228 | "telegraf": { 229 | "version": "4.1.1", 230 | "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.1.1.tgz", 231 | "integrity": "sha512-VGcFfTZzjlZquBHX5VwIzhREnDHQSHV+PkirqGKXzLWun/c/qkzJKeAFc3qv5AGJBrdfNbVbHNtZT7mjpmMPtg==", 232 | "dev": true, 233 | "requires": { 234 | "abort-controller": "^3.0.0", 235 | "debug": "^4.3.1", 236 | "minimist": "^1.2.5", 237 | "module-alias": "^2.2.2", 238 | "node-fetch": "^2.6.1", 239 | "p-timeout": "^4.1.0", 240 | "sandwich-stream": "^2.0.2", 241 | "typegram": "^3.1.10" 242 | } 243 | }, 244 | "typegram": { 245 | "version": "3.1.10", 246 | "resolved": "https://registry.npmjs.org/typegram/-/typegram-3.1.10.tgz", 247 | "integrity": "sha512-VfsuVP4uqk9yllRpaGp9yFpzvLKXghFCOSc3TVi+fy1IL2Bi4z2gJ9rMgVSSug4pWQP8Ui7Isvl14yYV8VEWaQ==", 248 | "dev": true 249 | }, 250 | "typescript": { 251 | "version": "4.2.2", 252 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.2.tgz", 253 | "integrity": "sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==", 254 | "dev": true 255 | }, 256 | "util-deprecate": { 257 | "version": "1.0.2", 258 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 259 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegraf-session-mongodb", 3 | "version": "2.0.2", 4 | "description": "MongoDB session middleware for Telegraf", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "clean": "rm -rf ./dist", 9 | "build": "tsc", 10 | "build:clean": "npm run clean && npm run build", 11 | "prepublish": "npm run build:clean", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+ssh://git@github.com/alexnzarov/telegraf-session-mongodb.git" 17 | }, 18 | "keywords": [ 19 | "telegraf", 20 | "mongo", 21 | "mongodb", 22 | "telegraf-session", 23 | "telegram", 24 | "middleware", 25 | "bot" 26 | ], 27 | "author": { 28 | "name": "alexnzarov", 29 | "email": "alexnzarov@gmail.com" 30 | }, 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/alexnzarov/telegraf-session-mongodb/issues" 34 | }, 35 | "homepage": "https://github.com/alexnzarov/telegraf-session-mongodb#readme", 36 | "dependencies": { 37 | "mongodb": "^3.6.4" 38 | }, 39 | "devDependencies": { 40 | "@types/mongodb": "^3.6.8", 41 | "telegraf": "^4.1.1", 42 | "typescript": "^4.2.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Db } from 'mongodb'; 2 | import { Context, MiddlewareFn } from 'telegraf'; 3 | import { getSessionKey, SessionKeyFunction } from './keys'; 4 | 5 | export type SessionOptions = { 6 | sessionName: string; 7 | collectionName: string; 8 | sessionKeyFn: SessionKeyFunction; 9 | }; 10 | 11 | export const session = (db: Db, sessionOptions?: Partial): MiddlewareFn => { 12 | const options: SessionOptions = { 13 | sessionName: 'session', 14 | collectionName: 'sessions', 15 | sessionKeyFn: getSessionKey, 16 | ...sessionOptions 17 | }; 18 | 19 | const collection = db.collection(options.collectionName); 20 | 21 | const saveSession = (key: string, data: any) => collection.updateOne({ key }, { $set: { data } }, { upsert: true }); 22 | const getSession = async (key: string) => (await collection.findOne({ key }))?.data ?? {}; 23 | 24 | const { sessionKeyFn: getKey, sessionName } = options; 25 | 26 | return async (ctx: Context, next) => { 27 | const key = getKey(ctx); 28 | const data = key == null ? undefined : await getSession(key); 29 | 30 | ctx[sessionName] = data; 31 | 32 | await next(); 33 | 34 | if (ctx[sessionName] != null) { 35 | await saveSession(key, ctx[sessionName]); 36 | } 37 | }; 38 | } -------------------------------------------------------------------------------- /src/keys.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "telegraf"; 2 | 3 | export type SessionKeyFunction = (ctx: Context) => string; 4 | 5 | // Mocking the behaviour from the default memory session implementation. 6 | // Source: https://telegraf.js.org/modules.html#session 7 | 8 | // This is possible due to getters that fetch 'from' and 'chat' values from different sources. 9 | 10 | export const getSessionKey = ({ from, chat }: Context) => { 11 | if (from == null || chat == null) { 12 | return null; 13 | } 14 | 15 | return `${from.id}:${chat.id}`; 16 | }; 17 | 18 | /** 19 | * @deprecated Use getSessionKey function and follow the default logic from the official documentation. 20 | */ 21 | export const getLegacySessionKey = (ctx: Context) => { 22 | const { chat, callbackQuery, from } = ctx; 23 | 24 | if (chat && chat.type === 'channel' && !from) { 25 | return `ch:${chat.id}`; 26 | } 27 | 28 | const id = chat ? chat.id : (callbackQuery ? callbackQuery.chat_instance : from.id); 29 | 30 | return `${id}:${from.id}`; 31 | }; 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "allowSyntheticDefaultImports": true, 5 | "target": "es6", 6 | "noImplicitAny": false, 7 | "declaration": true, 8 | "outDir": "dist", 9 | "baseUrl": ".", 10 | "paths": { 11 | "*": [ 12 | "node_modules/*", 13 | "src/types/*" 14 | ], 15 | } 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ] 20 | } --------------------------------------------------------------------------------