improve URL and image handling, user/room objects, store device ID
This commit is contained in:
parent
28cb4fe513
commit
1ce6505f00
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user