├── .gitignore ├── .github ├── release-drafter.yml ├── dependabot.yml └── workflows │ └── release-drafter.yml ├── .npmignore ├── .kodiak.toml ├── .jshintrc ├── Jenkinsfile ├── src ├── scripts │ ├── utils.js │ └── hubot-brain-redis-hash.js └── test │ └── hubot-brain-redis-hash_test.js ├── README.md ├── LICENSE-MIT └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | npm-debug.log 3 | /out/ 4 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | template: | 2 | ## What’s Changed 3 | 4 | $CHANGES 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | 3 | .travis* 4 | History.md 5 | grunt.coffee 6 | grunt.js 7 | 8 | out/ 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "13:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.kodiak.toml: -------------------------------------------------------------------------------- 1 | # .kodiak.toml 2 | # Minimal config. version is the only required field. 3 | version = 1 4 | [merge.automerge_dependencies] 5 | versions = ["minor", "patch"] 6 | # only automerge by upgrade version for pull requests authored by dependabot. 7 | usernames = ["dependabot"] 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "node": true, 14 | "es5": true 15 | } 16 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | docker { 4 | image 'node:lts' 5 | } 6 | } 7 | 8 | options { 9 | timeout(time: 10, unit: 'MINUTES') 10 | ansiColor('xterm') 11 | } 12 | 13 | stages { 14 | stage('Install') { 15 | steps { 16 | sh 'npm ci' 17 | } 18 | } 19 | 20 | stage('Test') { 21 | steps { 22 | sh 'npm run test' 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /src/scripts/utils.js: -------------------------------------------------------------------------------- 1 | const Redis = require("redis"); 2 | 3 | module.exports = {} 4 | module.exports.createClient = function() { 5 | let client; 6 | const info = Url.parse(process.env.REDISTOGO_URL || process.env.BOXEN_REDIS_URL || process.env.REDISCLOUD_URL || 'redis://localhost:6379'); 7 | 8 | if (info.hostname === '') { 9 | client = Redis.createClient(info.pathname); 10 | } else { 11 | client = Redis.createClient(info.port, info.hostname); 12 | } 13 | 14 | if (info.auth) { 15 | client.auth(info.auth.split(":")[1]); 16 | } 17 | 18 | return client; 19 | }; 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Migrated to https://github.com/halkeye/hubot-plugins/tree/master/packages/hubot-brain-redis-hash 2 | 3 | # hubot-brain-redis-hash 4 | 5 | Hubot brain that uses hset and hget instead of storing everything as one giant blob 6 | 7 | ## Getting Started 8 | 1. Install the module: `npm install --save hubot-brain-redis-hash` 9 | 2. Add it `hubot-brain-redis-hash` to your external-scripts.json file in your hubot directory 10 | 3. Profit 11 | 12 | ## Configuration (optional) 13 | ``` 14 | export REDISTOGO_URL="redis://localhost:6379" 15 | ``` 16 | 17 | ## Release History 18 | 19 | 0.3.0 - 2018-08-15 20 | 21 | * Added support for connecting to redis by local socket - Thanks @jplindquist - #33 22 | 23 | 0.1.3 - 2016-09-10 24 | 25 | * Added REDIS_PREFIX env config - Thanks @erikzaadi 26 | 27 | 0.2.0 - 2017-09-09 28 | 29 | * Updates to most libraries/deps 30 | * Added REDISCLOUD_URL env config - Thanks @actionshrimp - #18 31 | 32 | ## License 33 | Copyright (c) 2013 Gavin Mogan 34 | Licensed under the MIT license. 35 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Gavin 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hubot-brain-redis-hash", 3 | "description": "Hubot brain that uses hset and hget instead of storing everything as one giant blob", 4 | "version": "0.3.0", 5 | "homepage": "https://github.com/halkeye/hubot-brain-redis-hash", 6 | "author": { 7 | "name": "Gavin Mogan", 8 | "email": "npm@gavinmogan.com", 9 | "url": "http://www.gavinmogan.com" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/halkeye/hubot-brain-redis-hash.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/halkeye/hubot-brain-redis-hash/issues" 17 | }, 18 | "licenses": [ 19 | { 20 | "type": "MIT", 21 | "url": "https://github.com/halkeye/hubot-brain-redis-hash/blob/master/LICENSE-MIT" 22 | } 23 | ], 24 | "main": "src/scripts/hubot-brain-redis-hash", 25 | "engines": { 26 | "node": ">= 4" 27 | }, 28 | "scripts": { 29 | "test": "mocha" 30 | }, 31 | "devDependencies": { 32 | "fakeredis": "^2.0.0", 33 | "hubot": "^3.3.2", 34 | "hubot-test-helper": "^1.9.0", 35 | "mocha": "^9.0.2", 36 | "should": "~13.2.3", 37 | "sinon": "^11.1.1" 38 | }, 39 | "keywords": [ 40 | "hubot", 41 | "hubot-scripts" 42 | ], 43 | "dependencies": { 44 | "redis": "~3.1" 45 | }, 46 | "mocha": { 47 | "require": [ 48 | "should" 49 | ], 50 | "recursive": true, 51 | "reporter": "spec", 52 | "spec": "src/test/**/*.js" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/hubot-brain-redis-hash_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | process.env.HUBOT_LOG_LEVEL="alert"; 4 | process.env.EXPRESS_PORT = (process.env.PORT = 0); 5 | 6 | const Hubot = require('hubot'); 7 | const Path = require('path'); 8 | const Url = require('url'); 9 | const should = require('should'); 10 | const sinon = require('sinon'); 11 | const fakeredis = require('fakeredis'); 12 | const {promisify} = require('util'); 13 | 14 | const BrainUtilities = require('../scripts/utils'); 15 | const Helper = require('hubot-test-helper'); 16 | const helper = new Helper('../scripts/hubot-brain-redis-hash.js'); 17 | 18 | const hubot_brain_redis_hash = require('../scripts/hubot-brain-redis-hash'); 19 | 20 | describe('Hubot-Brain-Redis-Hash', function(){ 21 | let room; 22 | let client; 23 | 24 | before(() => { 25 | client = fakeredis.createClient(); 26 | sinon.stub(BrainUtilities, 'createClient').returns(client) 27 | }); 28 | after(() => BrainUtilities.createClient.restore()); 29 | 30 | beforeEach(function () { 31 | room = helper.createRoom({ httpd: false }); 32 | }); 33 | 34 | afterEach(function(done){ 35 | client.flushdb(() => done()); 36 | }); 37 | 38 | it('handleError', function(){ 39 | client.emit('error', 'this is my fake error'); 40 | }); 41 | 42 | it('handleConnect', function(done){ 43 | const orig_data = { users: {}, _private: {} }; 44 | room.robot.brain.data.should.eql(orig_data); 45 | orig_data.thisisgavin = { a: "byebye" }; 46 | 47 | const multi = client.multi(); 48 | for (let key of Object.keys(orig_data)) { 49 | const value = orig_data[key]; 50 | const a = multi.hset("hubot:brain", key, JSON.stringify(orig_data[key])); 51 | } 52 | 53 | multi.exec((err, replies) => { 54 | room.robot.brain.on('loaded', function(data) { 55 | data.should.eql(orig_data); 56 | done(); 57 | }); 58 | client.emit('connect'); 59 | }); 60 | }); 61 | 62 | it('handleSave', async function(){ 63 | room.robot.brain.data.should.eql({ users: {}, _private: {} }); 64 | room.robot.brain.data.gavin = "gavin"; 65 | room.robot.brain.data.gavin.should.eql("gavin"); 66 | room.robot.brain.save(); 67 | await new Promise(resolve => room.robot.brain.once('done:save', resolve)); 68 | const reply = await new Promise(resolve => client.hgetall("hubot:brain", function (err, values) { resolve(values); })); 69 | reply.gavin.should.eql('"gavin"'); 70 | }); 71 | }); 72 | 73 | -------------------------------------------------------------------------------- /src/scripts/hubot-brain-redis-hash.js: -------------------------------------------------------------------------------- 1 | // Description: 2 | // None 3 | // 4 | // Dependencies: 5 | // "redis": "0.7.2" 6 | // 7 | // Configuration: 8 | // REDISTOGO_URL 9 | // REDIS_PREFIX - If not provided will default to 'hubot'. 10 | // 11 | // Commands: 12 | // None 13 | // 14 | // Author: 15 | // Gavin Mogan 16 | 17 | 'use strict'; 18 | 19 | const Url = require("url"); 20 | const BrainUtilities = require('./utils'); 21 | 22 | // sets up hooks to persist the brain into redis. 23 | module.exports = function(robot) { 24 | robot.brain.redis_hash = {}; 25 | const client = BrainUtilities.createClient(); 26 | const prefix = process.env.REDIS_PREFIX || 'hubot'; 27 | 28 | let oldkeys = {}; 29 | client.on("error", err => robot.logger.error(err)); 30 | 31 | client.on("connect", function() { 32 | robot.logger.debug("Successfully connected to Redis"); 33 | 34 | return client.hgetall(`${prefix}:brain`, function(err, reply) { 35 | if (err) { 36 | throw err; 37 | } else if (reply) { 38 | robot.logger.info("Brain data retrieved from redis-brain storage"); 39 | const results = {}; 40 | oldkeys = {}; 41 | for (let key of Array.from(Object.keys(reply))) { 42 | results[key] = JSON.parse(reply[key].toString()); 43 | oldkeys[key] = 1; 44 | } 45 | robot.brain.mergeData(results); 46 | } else { 47 | robot.logger.info("Initializing new redis-brain storage"); 48 | robot.brain.mergeData({}); 49 | } 50 | 51 | robot.logger.info("Enabling brain auto-saving"); 52 | if (robot.brain.setAutoSave != null) { 53 | return robot.brain.setAutoSave(true); 54 | } 55 | }); 56 | }); 57 | 58 | // Prevent autosaves until connect has occured 59 | robot.logger.info("Disabling brain auto-saving"); 60 | if (robot.brain.setAutoSave != null) { 61 | robot.brain.setAutoSave(false); 62 | } 63 | 64 | robot.brain.on('save', function(data) { 65 | let key; 66 | if (data == null) { data = {}; } 67 | robot.logger.debug("Saving brain data"); 68 | const multi = (client.multi)(); 69 | const keys = Object.keys(data); 70 | const jsonified = {}; 71 | for (key of Array.from(keys)) { 72 | jsonified[key] = JSON.stringify(data[key]); 73 | } 74 | for (key of Array.from(oldkeys)) { 75 | if (!jsonified[key]) { 76 | multi.hdel(`${prefix}:brain`, key); 77 | } 78 | } 79 | 80 | oldkeys = {}; 81 | for (key of Array.from(keys)) { 82 | if (jsonified[key] != null) { 83 | oldkeys[key] = 1; 84 | multi.hset(`${prefix}:brain`, key, jsonified[key]); 85 | } 86 | } 87 | 88 | return multi.exec(function(err,replies) { 89 | robot.brain.emit('done:save'); 90 | }); 91 | }); 92 | 93 | robot.brain.on('close', () => client.quit()); 94 | 95 | return this; 96 | }; 97 | --------------------------------------------------------------------------------