├── .gitignore ├── LICENSE ├── README.md ├── assets ├── avatar.jpg ├── logo.jpg └── screenshot.png ├── bot.js ├── config.js ├── config_example.json ├── index.js ├── oauth.js ├── package.json ├── server.js └── user.js /.gitignore: -------------------------------------------------------------------------------- 1 | config.json 2 | node_modules 3 | *.log 4 | db 5 | .idea/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Pavel Glushkov 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Forwardr Logo 3 |

4 | 5 | # Forwardr 6 | 7 | [![Telegram: @ForwardrBot](https://img.shields.io/badge/use-@ForwardrBot-blue.svg?colorB=0088cc&style=flat)](https://telegram.me/ForwardrBot) 8 | [![Twitter: @pashutk](https://img.shields.io/badge/contact-@pashutk-blue.svg?colorB=1da1f2&style=flat)](https://twitter.com/pashutk) 9 | [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/pashutk/forwardr/blob/master/LICENSE) 10 | 11 | #### Simple to use Telegram-to-Tumblr bot 12 | 13 | ## Usage 14 | 15 | You can use existing bot [@ForwardrBot](https://telegram.me/forwardrbot) or run your own. 16 | 17 | To start the bot: 18 | - Register your bot. Talk to [BotFather](https://telegram.me/botfather) and generate an authorization token. 19 | - Register Tumblr app. Go to [Tumblr Apps page](https://www.tumblr.com/oauth/apps), create new app and get `OAuth Consumer Key` and `Secret Key` 20 | - Clone repository `git clone https://github.com/pashutk/forwardr.git && cd forwardr` 21 | - Install dependencies `npm install` 22 | - Run bot `npm start` and don't forget to specify tokens with environment variables or config file (see [section below](#variables)) 23 | 24 | ### Variables 25 | 26 | To run your bot you need to specify bot token, tumblr app consumer key and secret key (and optionally oauth redirect host for 27 | oauth authentification callback). You can set keys with [Environment variables](https://en.wikipedia.org/wiki/Environment_variable) or write them to `config.json` file. In case of using config file, use `config_example.json` as an example. 28 | 29 | List of keys: 30 | - `FORWARDR_TELEGRAM_BOT_TOKEN` 31 | - `FORWARDR_TUMBLR_CONSUMER_KEY` 32 | - `FORWARDR_TUMBLR_CONSUMER_SECRET` 33 | - `FORWARDR_SERVER_AUTH_HOST` (default is `http://localhost`) 34 | 35 | Also you can set `FORWARDR_SERVER_PORT` key to specify application port. 36 | 37 | ## Screenshot 38 | 39 |

40 | Forwardr Screenshot 41 |

42 | 43 | ## Contacts 44 | 45 | Feel free to contact me at [telegram](https://telegram.me/pashutk) or [twitter](https://twitter.com/pashutk) 46 | -------------------------------------------------------------------------------- /assets/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pashutk/forwardr/b8db6f2c7c8cbaae193cc1842d750c5211057896/assets/avatar.jpg -------------------------------------------------------------------------------- /assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pashutk/forwardr/b8db6f2c7c8cbaae193cc1842d750c5211057896/assets/logo.jpg -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pashutk/forwardr/b8db6f2c7c8cbaae193cc1842d750c5211057896/assets/screenshot.png -------------------------------------------------------------------------------- /bot.js: -------------------------------------------------------------------------------- 1 | const config = require('./config'); 2 | const MetaInspector = require('node-metainspector'); 3 | const TelegramBot = require('node-telegram-bot-api'); 4 | const User = require('./user'); 5 | 6 | 7 | function formatInfoMessage(user, data) { 8 | const header = `Info about *${data.user.name}* account: 9 | 10 | _${data.user.likes}_ likes 11 | _${data.user.following}_ following 12 | _${data.user.blogs.length}_ blogs:`; 13 | 14 | const blogs = data.user.blogs.map((blog) => `${user.defaultBlog === blog.name ? '_(default blog)_\n' : ''}Blog title: *${blog.title}* 15 | Blog posts: *${blog.posts}* 16 | [Blog url](${blog.url})`); 17 | 18 | return `${header}\n\n${blogs.join('\n')}`; 19 | } 20 | 21 | async function getMetaData(url) { 22 | return await new Promise((resolve, reject) => { 23 | const client = new MetaInspector(url, { timeout: 5000 }); 24 | 25 | client.on("fetch", () => resolve(client)); 26 | client.on("error", reject); 27 | 28 | client.fetch(); 29 | }); 30 | } 31 | 32 | async function botMessageHandler(message) { 33 | const userId = message.chat.id; 34 | try { 35 | const user = await User.getUserById(userId); 36 | 37 | const isAuthorized = () => { 38 | if (!user.isAuthorized) { 39 | this.sendMessage(userId, `You need to log in your tumblr account. Use /start command.`); 40 | } 41 | 42 | return user.isAuthorized; 43 | } 44 | 45 | if (message.text === '/start') { 46 | this.sendMessage(userId, `One second...`); 47 | await user.setOAuthRequestToken(); 48 | this.sendMessage(userId, `To authorize follow this link: ${user.signUrl}`); 49 | return; 50 | } 51 | 52 | if (message.text === '/info') { 53 | if (!isAuthorized()) return; 54 | 55 | const infoData = await user.info; 56 | const response = formatInfoMessage(user, infoData); 57 | this.sendMarkdown(userId, response); 58 | return; 59 | } 60 | 61 | if (message.text === '/rmlast') { 62 | await user.removeLast(); 63 | this.sendMessage(userId, 'Last post removed.'); 64 | return; 65 | } 66 | 67 | if (message.text) { 68 | if (!isAuthorized()) return; 69 | 70 | // if message is just url 71 | let trimmedText = message.text.trim(); 72 | if (message.entities && message.entities[0].type === 'url' && trimmedText.length === message.entities[0].length) { 73 | const metaData = {}; 74 | try { 75 | let metaDataClient = await getMetaData(message.text); 76 | Object.assign(metaData, metaDataClient); 77 | } catch(metaDataError) { 78 | this.sendMessage(userId, `Meta data scrapping error: ${metaDataError.message}`); 79 | } 80 | 81 | const newPost = await user.postLink(trimmedText, metaData); 82 | const postLink = await user.getPostLink(newPost.id); 83 | this.sendMarkdown(userId, `New link post, [post link](${postLink}).`); 84 | return; 85 | } 86 | 87 | const newPost = await user.postText(message.text); 88 | const postLink = await user.getPostLink(newPost.id); 89 | this.sendMarkdown(userId, `New text post, [post link](${postLink}).`); 90 | return; 91 | } 92 | } catch(err) { 93 | console.log(err); 94 | this.sendMessage(userId, `Error: ${err.message}`); 95 | } 96 | } 97 | 98 | async function botPhotoHandler(message) { 99 | const userId = message.chat.id; 100 | try { 101 | const user = await User.getUserById(userId); 102 | 103 | if (!user.isAuthorized) { 104 | this.sendMessage(userId, `You need to log in your tumblr account. Use /start command.`); 105 | return; 106 | } 107 | 108 | const largestPhoto = message.photo.reduce((prev, next) => { 109 | if (next.width * next.height > prev.width * prev.height) { 110 | return next; 111 | } 112 | 113 | return prev; 114 | }, { 115 | width: 0, 116 | height: 0, 117 | }); 118 | const photoLink = await this.getFileLink(largestPhoto.file_id); 119 | const newPost = await user.postPhoto(photoLink); 120 | const postLink = await user.getPostLink(newPost.id); 121 | this.sendMarkdown(userId, `Posted new photo, [post link](${postLink}).`); 122 | } catch(err) { 123 | console.log(err); 124 | this.sendMessage(userId, `Error: ${err.message}`); 125 | } 126 | } 127 | 128 | async function botAudioHandler(message) { 129 | const userId = message.chat.id; 130 | try { 131 | const user = await User.getUserById(userId); 132 | 133 | if (!user.isAuthorized) { 134 | this.sendMessage(userId, `You need to log in your tumblr account. Use /start command.`); 135 | return; 136 | } 137 | 138 | const audioLink = await this.getFileLink(message.audio.file_id); 139 | let trackName = ''; 140 | if (message.audio.performer) { 141 | trackName += message.audio.performer; 142 | } 143 | 144 | if (message.audio.title) { 145 | if (message.audio.performer) { 146 | trackName += ' - '; 147 | } 148 | trackName += message.audio.title; 149 | } 150 | 151 | const newPost = await user.postAudio(audioLink, trackName); 152 | const postLink = await user.getPostLink(newPost.id); 153 | this.sendMarkdown(userId, `Posted new audio, [post link](${postLink}).`); 154 | } catch(err) { 155 | console.log(err); 156 | this.sendMessage(userId, `Error: ${err.message}`); 157 | } 158 | } 159 | 160 | function createBot() { 161 | const options = { 162 | polling: true 163 | }; 164 | 165 | TelegramBot.prototype.sendMarkdown = function(id, text, opt_options) { 166 | const options = Object.assign({}, opt_options, { parse_mode: 'Markdown' }); 167 | return this.sendMessage(id, text, options); 168 | }; 169 | 170 | const bot = new TelegramBot(config.FORWARDR_TELEGRAM_BOT_TOKEN, options); 171 | bot.on('message', botMessageHandler); 172 | bot.on('photo', botPhotoHandler); 173 | bot.on('audio', botAudioHandler); 174 | return bot; 175 | } 176 | 177 | module.exports = { 178 | createBot 179 | } 180 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | let config = {}; 4 | 5 | try { 6 | config = JSON.parse(fs.readFileSync('config.json', 'utf8')); 7 | } catch(err) {}; 8 | 9 | module.exports = Object.assign({}, { 10 | FORWARDR_SERVER_AUTH_HOST: 'http://localhost', 11 | FORWARDR_SERVER_PORT: 8000, 12 | FORWARDR_TUMBLR_AUTH_URL: 'https://www.tumblr.com/oauth/authorize', 13 | FORWARDR_TUMBLR_REQUEST_TOKEN_URL: 'https://www.tumblr.com/oauth/request_token', 14 | FORWARDR_TUMBLR_ACCESS_TOKEN_URL: 'https://www.tumblr.com/oauth/access_token', 15 | FORWARDR_TUMBLR_OAUTH_VERSION: '1.0A', 16 | FORWARDR_TUMBLR_SIGNATURE_METHOD: 'HMAC-SHA1', 17 | }, config, process.env); 18 | -------------------------------------------------------------------------------- /config_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "FORWARDR_TELEGRAM_BOT_TOKEN": "token", 3 | "FORWARDR_TUMBLR_CONSUMER_KEY": "key", 4 | "FORWARDR_TUMBLR_CONSUMER_SECRET": "secret", 5 | "FORWARDR_SERVER_AUTH_HOST": "localhost:9000" 6 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const createServer = require('./server').createServer; 2 | const createBot = require('./bot').createBot; 3 | const User = require('./user'); 4 | 5 | function getWelcomeMessage(user) { 6 | let string = `Now you can use Forwardr.\n`; 7 | if (user.defaultBlog) { 8 | string += `Default blog now is *${user.defaultBlog}*.\n`; 9 | string += `Just send me any message and I will post it to your Tumblr blog.`; 10 | } else { 11 | string += `You don't have blogs. Blog specific methods are unavailable.`; 12 | } 13 | 14 | string += '\n'; 15 | return string; 16 | } 17 | 18 | async function authHandler(id, token, secret) { 19 | const user = await User.getUserById(id); 20 | await user.setOAuthData(token, secret); 21 | await user.setOAuthAccessToken(); 22 | await user.setDefaultBlog(); 23 | this.sendMarkdown(id, getWelcomeMessage(user)); 24 | } 25 | 26 | async function main() { 27 | await User.initStore(); 28 | const server = await createServer(); 29 | const bot = createBot(); 30 | 31 | server.on('auth', authHandler.bind(bot)); 32 | } 33 | 34 | main(); 35 | -------------------------------------------------------------------------------- /oauth.js: -------------------------------------------------------------------------------- 1 | const config = require('./config'); 2 | const OAuth = require('oauth'); 3 | 4 | 5 | module.exports = new OAuth.OAuth( 6 | config.FORWARDR_TUMBLR_REQUEST_TOKEN_URL, 7 | config.FORWARDR_TUMBLR_ACCESS_TOKEN_URL, 8 | config.FORWARDR_TUMBLR_CONSUMER_KEY, 9 | config.FORWARDR_TUMBLR_CONSUMER_SECRET, 10 | config.FORWARDR_TUMBLR_OAUTH_VERSION, 11 | null, 12 | config.FORWARDR_TUMBLR_SIGNATURE_METHOD 13 | ); 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forwardr", 3 | "version": "0.2.0", 4 | "description": "Telegram-to-Tumblr bot", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node --harmony --harmony-async-await index.js" 9 | }, 10 | "author": "pashutk", 11 | "license": "MIT", 12 | "dependencies": { 13 | "nedb": "^1.8.0", 14 | "node-metainspector": "^1.3.0", 15 | "node-telegram-bot-api": "^0.24.0", 16 | "oauth": "^0.9.14", 17 | "tumblr.js": "^1.1.1" 18 | }, 19 | "engines": { 20 | "node": ">=7.0.0" 21 | }, 22 | "bugs": { 23 | "url" : "https://github.com/owner/project/issues" 24 | }, 25 | "repository": "pashutk/forwardr" 26 | } 27 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const config = require('./config'); 2 | const http = require('http'); 3 | const url = require('url'); 4 | 5 | function requestHandler(req, res) { 6 | const parsedUrl = url.parse(req.url, true); 7 | if (req.method === 'GET' && 8 | parsedUrl.pathname === '/callback' && 9 | parsedUrl.query.oauth_token && 10 | parsedUrl.query.oauth_verifier && 11 | parsedUrl.query.id) { 12 | this.emit( 13 | 'auth', 14 | parsedUrl.query.id, 15 | parsedUrl.query.oauth_token, 16 | parsedUrl.query.oauth_verifier 17 | ); 18 | res.end('You can close this page now. Forwardr will submit your authorization soon.'); 19 | return; 20 | } 21 | res.end('Something going wrong. Contact me – @pashutk in Telegram.'); 22 | } 23 | 24 | const createServer = async () => await new Promise((resolve, reject) => { 25 | const server = http.createServer(requestHandler); 26 | server.listen(config.FORWARDR_SERVER_PORT, function() { 27 | console.log(`Server listen on ${config.FORWARDR_SERVER_PORT}`); 28 | resolve(server); 29 | }); 30 | }); 31 | 32 | module.exports = { 33 | createServer 34 | } 35 | -------------------------------------------------------------------------------- /user.js: -------------------------------------------------------------------------------- 1 | const config = require('./config'); 2 | const Store = require('nedb'); 3 | const tumblr = require('tumblr.js'); 4 | const tumblrOAuth = require('./oauth'); 5 | 6 | let store = null; 7 | 8 | module.exports = class User { 9 | constructor(data) { 10 | if (!data.id) { 11 | throw new Error('User without id'); 12 | } 13 | 14 | this.id = data.id + ''; 15 | this.token = data.token || ''; 16 | this.secret = data.secret || ''; 17 | this.isAuthorized = data.isAuthorized || false; 18 | this.oAuthRequestToken = data.oAuthRequestToken || ''; 19 | this.oAuthRequestSecret = data.oAuthRequestSecret || ''; 20 | this.oAuthToken = data.oAuthToken || ''; 21 | this.oAuthSecret = data.oAuthSecret || ''; 22 | this.defaultBlog = data.defaultBlog || ''; 23 | } 24 | 25 | serialize() { 26 | return { 27 | id: this.id, 28 | token: this.token, 29 | secret: this.secret, 30 | isAuthorized: this.isAuthorized, 31 | oAuthRequestToken: this.oAuthRequestToken, 32 | oAuthRequestSecret: this.oAuthRequestSecret, 33 | oAuthToken: this.oAuthToken, 34 | oAuthSecret: this.oAuthSecret, 35 | defaultBlog: this.defaultBlog, 36 | } 37 | } 38 | 39 | async update() { 40 | try { 41 | return await new Promise((resolve, reject) => { 42 | const query = { 43 | id: this.id 44 | } 45 | 46 | store.update(query, this.serialize(), { upsert: true }, (err, numAffected, affectedDocuments, upsert) => { 47 | if (err) { 48 | reject(err); 49 | return; 50 | } 51 | 52 | resolve(null); 53 | }); 54 | }); 55 | } catch(err) { 56 | console.log('Update error'); 57 | console.log(err); 58 | } 59 | } 60 | 61 | /** 62 | * @param {string} token 63 | * @param {string} secret 64 | * @return {Promise} 65 | */ 66 | async setOAuthData(token, secret) { 67 | this.isAuthorized = true; 68 | this.oAuthToken = token; 69 | this.oAuthSecret = secret; 70 | return await this.update(); 71 | } 72 | 73 | async getOAuthRequestToken() { 74 | const options = { 75 | oauth_callback: `${config.FORWARDR_SERVER_AUTH_HOST}/callback?id=${this.id}`, 76 | }; 77 | 78 | return await new Promise((resolve, reject) => { 79 | tumblrOAuth.getOAuthRequestToken(options, (err, token, secret, results) => { 80 | if (err) { 81 | reject(err); 82 | return; 83 | } 84 | 85 | resolve({ token, secret }); 86 | }); 87 | }); 88 | } 89 | 90 | async setOAuthRequestToken() { 91 | const oAuthRequestData = await this.getOAuthRequestToken(); 92 | this.oAuthRequestToken = oAuthRequestData.token; 93 | this.oAuthRequestSecret = oAuthRequestData.secret; 94 | return await this.update(); 95 | } 96 | 97 | async getOAuthAccessToken() { 98 | return await new Promise((resolve, reject) => { 99 | tumblrOAuth.getOAuthAccessToken( 100 | this.oAuthToken, 101 | this.oAuthRequestSecret, 102 | this.oAuthSecret, 103 | (err, token, secret) => { 104 | if (err) { 105 | console.error('\tValidation failed with error', err); 106 | reject(err); 107 | return; 108 | } 109 | 110 | resolve({ token, secret }); 111 | } 112 | ); 113 | }); 114 | } 115 | 116 | async setOAuthAccessToken() { 117 | const credentials = await this.getOAuthAccessToken(); 118 | this.token = credentials.token; 119 | this.secret = credentials.secret; 120 | return await this.update(); 121 | } 122 | 123 | get tumblrClient() { 124 | return tumblr.createClient({ 125 | credentials: { 126 | consumer_key: config.FORWARDR_TUMBLR_CONSUMER_KEY, 127 | consumer_secret: config.FORWARDR_TUMBLR_CONSUMER_SECRET, 128 | token: this.token, 129 | token_secret: this.secret, 130 | }, 131 | returnPromises: true, 132 | }) 133 | } 134 | 135 | get signUrl() { 136 | return tumblrOAuth.signUrl(config.FORWARDR_TUMBLR_AUTH_URL, this.oAuthRequestToken, this.oAuthRequestSecret); 137 | } 138 | 139 | /** @return {Promise} */ 140 | get info() { 141 | return this.tumblrClient.userInfo(); 142 | } 143 | 144 | /** @return {Promise} */ 145 | get blogPosts() { 146 | return this.tumblrClient.blogPosts(this.defaultBlog); 147 | } 148 | 149 | async getPostLink(id) { 150 | const filteredData = await this.tumblrClient.blogPosts(this.defaultBlog, { id }); 151 | if (!filteredData.posts.length) { 152 | return; 153 | } 154 | 155 | return filteredData.posts[0].post_url; 156 | } 157 | 158 | async setDefaultBlog() { 159 | // TODO: support multiple blogs 160 | const info = await this.info; 161 | 162 | if (info.user.blogs.length === 0) { 163 | return; 164 | } 165 | 166 | this.defaultBlog = info.user.blogs[0].name; 167 | return await this.update(); 168 | } 169 | 170 | /** 171 | * @param {string} text 172 | * @return {Promise} 173 | */ 174 | async postText(text) { 175 | return await this.tumblrClient.createTextPost(this.defaultBlog, { 176 | body: text 177 | }); 178 | } 179 | 180 | /** 181 | * @TODO: add title and artist support 182 | * @param {string} link 183 | * @param {string} opt_caption 184 | * @return {Promise} 185 | */ 186 | async postAudio(link, opt_caption) { 187 | const options = { 188 | external_url: link 189 | }; 190 | 191 | if (opt_caption) { 192 | options.caption = opt_caption; 193 | } 194 | 195 | return await this.tumblrClient.createAudioPost(this.defaultBlog, options); 196 | } 197 | 198 | /** 199 | * @param {string} link 200 | * @return {Promise} 201 | */ 202 | async postPhoto(link) { 203 | return await this.tumblrClient.createPhotoPost(this.defaultBlog, { 204 | source: link 205 | }); 206 | } 207 | 208 | /** 209 | * @param {string} link 210 | * @param {Object} metaData 211 | * @return {Promise} 212 | */ 213 | async postLink(link, metaData) { 214 | const options = { 215 | url: link 216 | }; 217 | 218 | if (metaData.title) { 219 | options.title = metaData.title; 220 | } 221 | 222 | if (metaData.image) { 223 | options.thumbnail = metaData.image; 224 | } 225 | 226 | if (metaData.description) { 227 | options.excerpt = metaData.description; 228 | } 229 | 230 | if (metaData.author) { 231 | options.author = metaData.author; 232 | } 233 | 234 | return await this.tumblrClient.createLinkPost(this.defaultBlog, options); 235 | } 236 | 237 | async removeLast() { 238 | const data = await this.blogPosts; 239 | const posts = data.posts; 240 | if (posts.length === 0) { 241 | throw new Error('No posts'); 242 | } 243 | 244 | return await this.tumblrClient.deletePost(this.defaultBlog, posts[0].id); 245 | } 246 | 247 | /** 248 | * @param {number|string} id 249 | * @return {User} 250 | */ 251 | static createNew(id) { 252 | return new User({ 253 | id, 254 | token: '', 255 | secret: '', 256 | isAuthorized: false, 257 | oAuthRequestToken: '', 258 | oAuthRequestSecret: '', 259 | oAuthToken: '', 260 | oAuthSecret: '', 261 | }); 262 | } 263 | 264 | /** 265 | * @param {number|string} id 266 | * @return {User} 267 | */ 268 | static async getUserById(id) { 269 | if (typeof id === 'number') { 270 | id += ''; 271 | } 272 | 273 | return await new Promise((resolve, reject) => { 274 | if (!store) { 275 | reject('No store'); 276 | return; 277 | } 278 | 279 | const query = { id }; 280 | store.findOne(query, function (err, doc) { 281 | if (err) { 282 | reject(err); 283 | return; 284 | } 285 | 286 | if (doc) { 287 | resolve(new User(doc)); 288 | } else { 289 | resolve(User.createNew(id)); 290 | } 291 | }); 292 | }); 293 | } 294 | 295 | /** @return {Promise} */ 296 | static async initStore() { 297 | return await new Promise((resolve, reject) => { 298 | const options = { 299 | filename: 'db' 300 | } 301 | store = new Store(options); 302 | store.loadDatabase(function(err) { 303 | if (err) { 304 | reject(err); 305 | return; 306 | } 307 | 308 | console.log('Store inited'); 309 | resolve(null); 310 | }); 311 | }); 312 | } 313 | } 314 | --------------------------------------------------------------------------------