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) // 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"` }