2020-02-16 15:26:55 +00:00
|
|
|
package connector
|
|
|
|
|
2020-02-21 14:12:22 +00:00
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
/*
|
|
|
|
A generic connector framework for instant messaging protocols.
|
|
|
|
|
|
|
|
Model:
|
|
|
|
|
|
|
|
- A connector represents a connection to an outgoing service (IRC, XMPP, etc)
|
|
|
|
It satisfies a generic interface representing the actions that can be called
|
|
|
|
(send messages, join room, etc)
|
2020-02-17 18:02:26 +00:00
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
- A handler represents a consumer of events happening on a connection
|
|
|
|
It satisfies a generic interface representing the events that can happend
|
|
|
|
(message received, rooms autojoined, etc)
|
2020-02-17 18:02:26 +00:00
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
- A connector implements a given protocol that has an identifier
|
|
|
|
Each protocol identifier determines a namespace for user identifiers
|
|
|
|
and room identifiers which are globally unique for all connections using
|
|
|
|
this protocol.
|
|
|
|
For instance, a user can have two IRC conections to different servers.
|
|
|
|
Internally used user names and room identifiers must contain
|
|
|
|
the server name to be able to differentiate.
|
|
|
|
*/
|
|
|
|
|
|
|
|
type UserID string
|
|
|
|
type RoomID string
|
|
|
|
|
|
|
|
type Connector interface {
|
|
|
|
// Set the handler that will receive events happening on this connection
|
|
|
|
SetHandler(handler Handler)
|
|
|
|
|
|
|
|
// Configure (or reconfigure) the connector and attempt to connect
|
|
|
|
Configure(conf Configuration) error
|
|
|
|
|
|
|
|
// Get the identifier of the protocol that is implemented by this connector
|
|
|
|
Protocol() string
|
|
|
|
|
|
|
|
// Get the user id of the connected user
|
|
|
|
User() UserID
|
|
|
|
|
|
|
|
// Set user information (nickname, picture, etc)
|
|
|
|
SetUserInfo(info *UserInfo) error
|
|
|
|
|
|
|
|
// Set room information (name, description, picture, etc)
|
|
|
|
SetRoomInfo(roomId RoomID, info *RoomInfo) error
|
|
|
|
|
|
|
|
// Try to join a channel
|
|
|
|
// If no error happens, it must fire a Handler.Joined event
|
|
|
|
Join(roomId RoomID) error
|
|
|
|
|
2020-02-16 15:41:13 +00:00
|
|
|
// Try to invite someone to a channel
|
2020-02-17 20:04:21 +00:00
|
|
|
// Or if roomId == "", just try adding them as friends
|
2020-02-16 15:41:13 +00:00
|
|
|
Invite(user UserID, roomId RoomID) error
|
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
// Leave a channel
|
|
|
|
Leave(roomId RoomID)
|
|
|
|
|
2020-03-09 16:41:53 +00:00
|
|
|
// Search for users
|
|
|
|
SearchForUsers(query string) ([]UserSearchResult, error)
|
|
|
|
|
2020-02-29 09:01:42 +00:00
|
|
|
// Send an event. Returns the ID of the created remote message.
|
|
|
|
// This ID is used to deduplicate messages: if it comes back, it should have the same Id
|
|
|
|
// than the one returned here.
|
|
|
|
// For backends that do not implement IDs (e.g. IRC), an empty string is returned.
|
|
|
|
// (FIXME how to deduplicate IRC messages?)
|
|
|
|
// The event that is fed in this function may have its ID already set,
|
|
|
|
// in which case the backend is free to re-use the ID or select a new one.
|
|
|
|
Send(event *Event) (string, error)
|
2020-02-16 15:26:55 +00:00
|
|
|
|
|
|
|
// Close the connection
|
|
|
|
Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Handler interface {
|
2020-03-09 17:02:13 +00:00
|
|
|
// Called to save updated configuration parameters
|
|
|
|
SaveConfig(config Configuration)
|
|
|
|
|
2020-05-04 16:36:55 +00:00
|
|
|
// Called to notify user of stuff going on
|
|
|
|
SystemMessage(message string)
|
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
// Called when a room was joined (automatically or by call to Connector.Join)
|
|
|
|
Joined(roomId RoomID)
|
|
|
|
|
|
|
|
// Called when the user left a room
|
|
|
|
Left(roomId RoomID)
|
|
|
|
|
|
|
|
// Called when a user's info is updated (changed their nickname, status, etc)
|
|
|
|
// Can also be called with our own user ID when first loaded our user info
|
|
|
|
UserInfoUpdated(user UserID, info *UserInfo)
|
|
|
|
|
|
|
|
// Called when a room's info was updated,
|
|
|
|
// or the first tome a room's info is retreived
|
2020-02-17 14:30:01 +00:00
|
|
|
RoomInfoUpdated(roomId RoomID, author UserID, info *RoomInfo)
|
2020-02-16 15:26:55 +00:00
|
|
|
|
|
|
|
// Called when an event occurs in a room
|
|
|
|
// This must not be called for events authored by the user of the connection
|
|
|
|
Event(event *Event)
|
2020-02-21 18:36:16 +00:00
|
|
|
|
|
|
|
// These two functions enable the connector to access a simple key/value
|
|
|
|
// database to cache some information in order not to generate useless events.
|
|
|
|
// The connector should function when they are not implemented,
|
|
|
|
// in which case CacheGet always returns ""
|
|
|
|
CachePut(key string, value string)
|
|
|
|
CacheGet(key string) string
|
2020-02-16 15:26:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type EventType int
|
2020-02-17 18:02:26 +00:00
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
const (
|
|
|
|
EVENT_JOIN EventType = iota
|
|
|
|
EVENT_LEAVE
|
|
|
|
EVENT_MESSAGE
|
|
|
|
EVENT_ACTION
|
|
|
|
)
|
|
|
|
|
|
|
|
type Event struct {
|
2020-02-29 17:30:43 +00:00
|
|
|
Type EventType `json:"type"`
|
2020-02-16 15:26:55 +00:00
|
|
|
|
2020-02-21 18:28:00 +00:00
|
|
|
// If non-empty, the event Id is used to deduplicate events in a channel
|
|
|
|
// This is usefull for backends that provide a backlog of channel messages
|
|
|
|
// when (re-)joining a room
|
2020-02-29 17:30:43 +00:00
|
|
|
Id string `json:"id"`
|
2020-02-21 18:28:00 +00:00
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
// UserID of the user that sent the event
|
|
|
|
// If this is a direct message event, this event can only have been authored
|
|
|
|
// by the user we are talking to (and not by ourself)
|
2020-02-29 17:30:43 +00:00
|
|
|
Author UserID `json:"author"`
|
2020-02-16 15:26:55 +00:00
|
|
|
|
|
|
|
// UserID of the targetted user in the case of a direct message,
|
|
|
|
// empty if targetting a room
|
2020-02-29 17:30:43 +00:00
|
|
|
Recipient UserID `json:"recipient"`
|
2020-02-16 15:26:55 +00:00
|
|
|
|
|
|
|
// RoomID of the room where the event happenned or of the targetted room,
|
|
|
|
// or empty string if it happenned by direct message
|
2020-02-29 17:30:43 +00:00
|
|
|
Room RoomID `json:"room"`
|
2020-02-16 15:26:55 +00:00
|
|
|
|
|
|
|
// Message text or action text
|
2020-03-01 12:08:32 +00:00
|
|
|
Text string `json:"text"`
|
2020-02-16 15:26:55 +00:00
|
|
|
|
|
|
|
// Attached files such as images
|
2020-02-29 17:30:43 +00:00
|
|
|
Attachments []SMediaObject `json:"attachments"`
|
2020-02-16 15:26:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type UserInfo struct {
|
2020-02-29 17:30:43 +00:00
|
|
|
DisplayName string `json:"display_name"`
|
2020-02-21 18:28:00 +00:00
|
|
|
|
|
|
|
// If non-empty, the Filename of the avatar object will be used by Easybridge
|
|
|
|
// to deduplicate the update events and prevent needless reuploads.
|
|
|
|
// Example strategy that works for the mattermost backend: use the update timestamp as fictious file name
|
2020-02-29 17:30:43 +00:00
|
|
|
Avatar SMediaObject `json:"avatar"`
|
2020-02-16 15:26:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type RoomInfo struct {
|
2020-02-29 17:30:43 +00:00
|
|
|
Name string `json:"name"`
|
|
|
|
Topic string `json:"topic"`
|
2020-02-21 18:28:00 +00:00
|
|
|
|
|
|
|
// Same deduplication comment as for UserInfo.Avatar
|
2020-02-29 17:30:43 +00:00
|
|
|
Picture SMediaObject `json:"picture"`
|
2020-02-16 15:26:55 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 16:41:53 +00:00
|
|
|
type UserSearchResult struct {
|
|
|
|
ID UserID `json:"id"`
|
|
|
|
DisplayName string `json:"display_name"`
|
|
|
|
}
|
|
|
|
|
2020-02-16 15:26:55 +00:00
|
|
|
type MediaObject interface {
|
2020-02-21 14:12:22 +00:00
|
|
|
Filename() string
|
|
|
|
Size() int64
|
|
|
|
Mimetype() string
|
2020-02-16 15:26:55 +00:00
|
|
|
|
2020-02-21 14:57:53 +00:00
|
|
|
// Returns the size of an image if it is an image, otherwise nil
|
|
|
|
ImageSize() *ImageSize
|
|
|
|
|
2020-02-21 14:12:22 +00:00
|
|
|
// Read: must always be implemented
|
|
|
|
Read() (io.ReadCloser, error)
|
2020-02-16 15:26:55 +00:00
|
|
|
|
2020-02-21 14:12:22 +00:00
|
|
|
// URL(): not mandatory, may return an empty string
|
|
|
|
// If so, Read() is the only way to retrieve the object
|
|
|
|
URL() string
|
2020-02-16 15:26:55 +00:00
|
|
|
}
|
2020-02-21 14:57:53 +00:00
|
|
|
|
2020-02-29 17:30:43 +00:00
|
|
|
type SMediaObject struct {
|
|
|
|
MediaObject
|
|
|
|
}
|
|
|
|
|
2020-02-21 14:57:53 +00:00
|
|
|
type ImageSize struct {
|
2020-02-29 17:30:43 +00:00
|
|
|
Width int `json:"width"`
|
|
|
|
Height int `json:"height"`
|
2020-02-21 14:57:53 +00:00
|
|
|
}
|