improve URL and image handling, user/room objects, store device ID

This commit is contained in:
rnhmjoj 2017-02-26 23:51:34 +01:00
parent 28cb4fe513
commit 1ce6505f00
No known key found for this signature in database
GPG Key ID: 362BB82B7E496B7C

View File

@ -1,21 +1,28 @@
try try
{Robot,Adapter,TextMessage,User} = require 'hubot' { Robot
, Adapter
, TextMessage } = require 'hubot'
catch catch
prequire = require('parent-require') prequire = require 'parent-require'
{Robot,Adapter,TextMessage,User} = prequire 'hubot' { Robot
, Adapter
, TextMessage } = prequire 'hubot'
{ LocalStorage } = require 'node-localstorage'
sdk = require 'matrix-js-sdk' sdk = require 'matrix-js-sdk'
request = require 'request' ne = require 'needle'
sizeOf = require 'image-size' syn = require 'async'
gm = require 'gm'
url = require 'url'
unless localStorage?
{LocalStorage} = require('node-localstorage')
localStorage = new LocalStorage('./hubot-matrix.localStorage')
class Matrix extends Adapter class Matrix extends Adapter
constructor: -> constructor: ->
super super
@robot.logger.info "Constructor" @local_storage = new LocalStorage process.env.HUBOT_MATRIX_DATA || 'matrix-data'
@text = []
handleUnknownDevices: (err) -> handleUnknownDevices: (err) ->
for stranger, devices of err.devices for stranger, devices of err.devices
@ -23,95 +30,165 @@ class Matrix extends Adapter
@robot.logger.info "Acknowledging #{stranger}'s device #{device}" @robot.logger.info "Acknowledging #{stranger}'s device #{device}"
@client.setDeviceKnown(stranger, device) @client.setDeviceKnown(stranger, device)
send: (envelope, strings...) ->
for str in strings handleURL: (envelope) -> (line, done) =>
@robot.logger.info "Sending to #{envelope.room}: #{str}" # supported image mime types
if /^(f|ht)tps?:\/\//i.test(str) accepted = ['image/jpeg', 'image/png', 'image/tiff']
@sendURL envelope, str
else
@client.sendNotice(envelope.room, str).catch (err) => if not url.parse(line).hostname
return @sendText envelope, line, -> done()
# fetch headers
ne.head line, follow_max: 5, (err, res) =>
@robot.logger.info 'found url ' + line
if err?
@robot.logger.warning "headers download failed:\n#{err}"
return @sendText envelope, line, -> done()
mimetype = res.headers['content-type'].split(';')[0]
if not (mimetype in accepted)
@robot.logger.info 'url ignored'
return @sendText envelope, line, -> done()
@robot.logger.info 'found image: downloading...'
@getImage line, (buffer, info) =>
@sendImage envelope, buffer, info, -> done()
getImage: (imageURL, callback) ->
# process the image a bit
ne.get imageURL, follow_max: 5, (err, res, body) =>
gm(body)
.noProfile()
.quality(80)
.resize(360000,'@>')
.toBuffer (err, buffer) =>
@robot.logger.info 'image downloaded and processed'
gm(buffer).identify "%m %w %h", (err, format) ->
[type, width, height] = format.split ' '
callback buffer,
mimetype: "image/" + type.toLowerCase()
w: width
h: height
size: buffer.length
url: imageURL
send: (envelope, lines...) ->
[..., last] = lines
if typeof last is 'function'
callback = lines.pop()
syn.eachSeries lines, (@handleURL envelope), ->
callback() if callback?
sendText: (envelope, text, callback) ->
@client.sendNotice(envelope.room.id, text).catch (err) =>
if err.name == 'UnknownDeviceError'
@handleUnknownDevices err
@client.sendNotice(envelope.room.id, text)
callback() if callback?
sendImage: (envelope, buffer, info, callback) ->
try
@client.uploadContent(buffer, name: info.url, type: info.mimetype, rawResponse: false, onlyContentUri: true).done (content_uri) =>
@client.sendImageMessage(envelope.room.id, content_uri, info, info.url).catch (err) =>
if err.name == 'UnknownDeviceError' if err.name == 'UnknownDeviceError'
@handleUnknownDevices err @handleUnknownDevices err
@client.sendNotice(envelope.room, str) @client.sendImageMessage(envelope.room.id, content_uri, info, info.url)
catch error
@robot.logger.info "image upload failed: #{error.message}"
finally
@robot.logger.info 'image sent'
callback() if callback?
emote: (envelope, strings...) ->
for str in strings emote: (envelope, lines...) ->
@client.sendEmoteMessage(envelope.room, str).catch (err) => for line in lines
@client.sendEmoteMessage(envelope.room.id, line).catch (err) =>
if err.name == 'UnknownDeviceError' if err.name == 'UnknownDeviceError'
@handleUnknownDevices err @handleUnknownDevices err
@client.sendEmoteMessage(envelope.room, str) @client.sendEmoteMessage(envelope.room.id, line)
reply: (envelope, strings...) ->
for str in strings
@send envelope, "#{envelope.user.name}: #{str}"
topic: (envelope, strings...) -> reply: (envelope, lines...) ->
for str in strings for line in lines
@client.sendStateEvent envelope.room, "m.room.topic", { @send envelope, "#{envelope.user.name}: #{line}"
topic: str
}, ""
topic: (envelope, lines...) ->
for line in lines
@client.sendStateEvent envelope.room.id, "m.room.topic", topic: line, ""
sendURL: (envelope, url) ->
@robot.logger.info "Downloading #{url}"
request url: url, encoding: null, (error, response, body) =>
if error
@robot.logger.info "Request error: #{JSON.stringify error}"
else if response.statusCode == 200
try
dims = sizeOf body
@robot.logger.info "Image has dimensions #{JSON.stringify dims}, size #{body.length}"
dims.type = 'jpeg' if dims.type == 'jpg'
info = { mimetype: "image/#{dims.type}", h: dims.height, w: dims.width, size: body.length }
@client.uploadContent(body, name: url, type: info.mimetype, rawResponse: false, onlyContentUri: true).done (content_uri) =>
@client.sendImageMessage(envelope.room, content_uri, info, url).catch (err) =>
if err.name == 'UnknownDeviceError'
@handleUnknownDevices err
@client.sendImageMessage(envelope.room, content_uri, info, url)
catch error
@robot.logger.info error.message
@send envelope, " #{url}"
run: -> run: ->
@robot.logger.info "Run #{@robot.name}" @robot.logger.info "starting matrix adapter"
client = sdk.createClient(process.env.HUBOT_MATRIX_HOST_SERVER || 'https://matrix.org') client = sdk.createClient(process.env.HUBOT_MATRIX_HOST_SERVER || 'https://matrix.org')
client.login 'm.login.password', { client.login 'm.login.password',
user: @robot.name user: @robot.name
password: process.env.HUBOT_MATRIX_PASSWORD password: process.env.HUBOT_MATRIX_PASSWORD
}, (err, data) => , (err, data) =>
if err
@robot.logger.error err return @robot.logger.error err if err?
return
@user_id = data.user_id @user_id = data.user_id
@access_token = data.access_token @device_id = @local_storage.getItem 'device_id'
@device_id = data.device_id @device_id = data.device_id unless @device_id?
@robot.logger.info "Logged in #{@user_id} on device #{@device_id}" @access_token = data.access_token
@local_storage.setItem 'device_id', @device_id
@robot.logger.info "logged in #{@user_id} on device #{@device_id}"
@client = sdk.createClient @client = sdk.createClient
baseUrl: process.env.HUBOT_MATRIX_HOST_SERVER || 'https://matrix.org' baseUrl: process.env.HUBOT_MATRIX_HOST_SERVER || 'https://matrix.org'
accessToken: @access_token userId: @user_id
userId: @user_id deviceId: @device_id
deviceId: @device_id accessToken: @access_token
sessionStore: new sdk.WebStorageSessionStore(localStorage) sessionStore: new sdk.WebStorageSessionStore(@local_storage)
@client.on 'sync', (state, prevState, data) => @client.on 'sync', (state, prevState, data) =>
switch state switch state
when "PREPARED" when "PREPARED"
@robot.logger.info "Synced #{@client.getRooms().length} rooms" @robot.logger.info "synced #{@client.getRooms().length} rooms"
@emit 'connected' @emit 'connected'
createUser = (user) =>
id: user.userId
name: user.name
avatar: user.getAvatarUrl @client.baseUrl, 120, 120, allowDefault: false
@client.on 'Room.timeline', (event, room, toStartOfTimeline) => @client.on 'Room.timeline', (event, room, toStartOfTimeline) =>
if event.getType() == 'm.room.message' and toStartOfTimeline == false if event.getType() == 'm.room.message' and toStartOfTimeline == false
@client.setPresence "online" @client.setPresence "online"
message = event.getContent()
name = event.getSender() message = event.getContent()
user = @robot.brain.userForId name user = @robot.brain.userForId event.sender.userId
user.room = room.roomId user.name = event.sender.name
if user.name != @user_id user.avatar = event.sender.getAvatarUrl @client.baseUrl, 120, 120, allowDefault: false
@robot.logger.info "Received message: #{JSON.stringify message} in room: #{user.room}, from: #{user.name}." user.room =
@receive new TextMessage user, message.body if message.msgtype == "m.text" id: room.roomId
@client.sendReadReceipt(event) if message.msgtype != "m.text" or message.body.indexOf(@robot.name) != -1 name: room.name
private: room.getJoinedMembers().length == 2
members: room.getJoinedMembers().map createUser
invitees: room.getMembersWithMembership("invite").map createUser
if user.id != @user_id
@receive new TextMessage user, message.body if message.msgtype == "m.text"
if message.msgtype != "m.text" or message.body.indexOf(@robot.name) != -1
@client.sendReadReceipt(event)
@client.on 'RoomMember.membership', (event, member) => @client.on 'RoomMember.membership', (event, member) =>
if member.membership == 'invite' and member.userId == @user_id if member.membership == 'invite' and member.userId == @user_id
@client.joinRoom(member.roomId).done => @client.joinRoom(member.roomId).done =>
@robot.logger.info "Auto-joined #{member.roomId}" @robot.logger.info "auto-joined #{member.roomId}"
@client.startClient 0 @client.startClient 0
exports.use = (robot) -> exports.use = (robot) ->
new Matrix robot new Matrix robot # sentinel