package connector
import (
"io"
)
/*
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)
- 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)
- 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
// Try to invite someone to a channel
// Or if roomId == "", just try adding them as friends
Invite(user UserID, roomId RoomID) error
// Leave a channel
Leave(roomId RoomID)
// Search for users
SearchForUsers(query string) ([]UserSearchResult, error)
// 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)
// Used to send user commands directly
// (first use case: receive 2-factor authentication codes)
UserCommand(string)
// Close the connection
Close()
}
type Handler interface {
// Called to save updated configuration parameters
SaveConfig(config Configuration)
// Called to notify user of stuff going on
SystemMessage(message string)
// 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
RoomInfoUpdated(roomId RoomID, author UserID, info *RoomInfo)
// 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)
// 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
}
type EventType int
const (
EVENT_JOIN EventType = iota
EVENT_LEAVE
EVENT_MESSAGE
EVENT_ACTION
)
type Event struct {
Type EventType `json:"type"`
// 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
Id string `json:"id"`
// 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)
Author UserID `json:"author"`
// UserID of the targetted user in the case of a direct message,
// empty if targetting a room
Recipient UserID `json:"recipient"`
// RoomID of the room where the event happenned or of the targetted room,
// or empty string if it happenned by direct message
Room RoomID `json:"room"`
// Message text or action text
Text string `json:"text"`
// Attached files such as images
Attachments []SMediaObject `json:"attachments"`
}
type UserInfo struct {
DisplayName string `json:"display_name"`
// 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
Avatar SMediaObject `json:"avatar"`
}
type RoomInfo struct {
Name string `json:"name"`
Topic string `json:"topic"`
// Same deduplication comment as for UserInfo.Avatar
Picture SMediaObject `json:"picture"`
}
type UserSearchResult struct {
ID UserID `json:"id"`
DisplayName string `json:"display_name"`
}
type MediaObject interface {
Filename() string
Size() int64
Mimetype() string
// Returns the size of an image if it is an image, otherwise nil
ImageSize() *ImageSize
// Read: must always be implemented
Read() (io.ReadCloser, error)
// URL(): not mandatory, may return an empty string
// If so, Read() is the only way to retrieve the object
URL() string
}
type SMediaObject struct {
MediaObject
}
type ImageSize struct {
Width int `json:"width"`
Height int `json:"height"`
}