lisb-hubot
Version:
A simple helpful robot for your Company
141 lines (111 loc) • 3.76 kB
JavaScript
const fs = require('fs')
const readline = require('readline')
const Stream = require('stream')
const cline = require('cline')
const Adapter = require('../adapter')
const _require = require('../message')
const TextMessage = _require.TextMessage
const historySize = process.env.HUBOT_SHELL_HISTSIZE != null ? parseInt(process.env.HUBOT_SHELL_HISTSIZE) : 1024
const historyPath = '.hubot_history'
const bold = str => `\x1b[1m${str}\x1b[22m`
class Shell extends Adapter {
constructor (robot) {
super(robot)
this.name = 'Shell'
}
send (envelope/* , ...strings */) {
const strings = [].slice.call(arguments, 1)
Array.from(strings).forEach(str => console.log(bold(str)))
}
emote (envelope/* , ...strings */) {
const strings = [].slice.call(arguments, 1)
Array.from(strings).map(str => this.send(envelope, `* ${str}`))
}
reply (envelope/* , ...strings */) {
const strings = [].slice.call(arguments, 1).map((s) => `${envelope.user.name}: ${s}`)
this.send.apply(this, [envelope].concat(strings))
}
run () {
this.buildCli()
loadHistory((error, history) => {
if (error) {
console.log(error.message)
}
this.cli.history(history)
this.cli.interact(`${this.robot.name}> `)
return this.emit('connected', this)
})
}
shutdown () {
this.robot.shutdown()
return process.exit(0)
}
buildCli () {
this.cli = cline()
this.cli.command('*', input => {
let userId = process.env.HUBOT_SHELL_USER_ID || '1'
if (userId.match(/A\d+z/)) {
userId = parseInt(userId)
}
const userName = process.env.HUBOT_SHELL_USER_NAME || 'Shell'
const user = this.robot.brain.userForId(userId, { name: userName, room: 'Shell' })
this.receive(new TextMessage(user, input, 'messageId'))
})
this.cli.command('history', () => {
Array.from(this.cli.history()).map(item => console.log(item))
})
this.cli.on('history', item => {
if (item.length > 0 && item !== 'exit' && item !== 'history') {
fs.appendFile(historyPath, `${item}\n`, error => {
if (error) {
this.robot.emit('error', error)
}
})
}
})
this.cli.on('close', () => {
let history, i, item, len
history = this.cli.history()
if (history.length <= historySize) {
return this.shutdown()
}
const startIndex = history.length - historySize
history = history.reverse().splice(startIndex, historySize)
const fileOpts = {
mode: 0x180
}
const outstream = fs.createWriteStream(historyPath, fileOpts)
outstream.on('end', this.shutdown.bind(this))
for (i = 0, len = history.length; i < len; i++) {
item = history[i]
outstream.write(item + '\n')
}
})
}
}
// Prevent output buffer "swallowing" every other character on OSX / Node version > 16.19.0.
process.stdout._handle.setBlocking(false)
exports.use = robot => new Shell(robot)
// load history from .hubot_history.
//
// callback - A Function that is called with the loaded history items (or an empty array if there is no history)
function loadHistory (callback) {
if (!fs.existsSync(historyPath)) {
return callback(new Error('No history available'))
}
const instream = fs.createReadStream(historyPath)
const outstream = new Stream()
outstream.readable = true
outstream.writable = true
const items = []
readline.createInterface({ input: instream, output: outstream, terminal: false })
.on('line', function (line) {
line = line.trim()
if (line.length > 0) {
items.push(line)
}
})
.on('close', () => callback(null, items))
.on('error', callback)
}