2018-06-13 22:41:05 +00:00
|
|
|
// maubot - A plugin-based Matrix bot system written in Go.
|
|
|
|
// Copyright (C) 2018 Tulir Asokan
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package maubot
|
|
|
|
|
2018-06-14 08:11:24 +00:00
|
|
|
type EventType string
|
|
|
|
type MessageType string
|
2018-06-13 22:41:05 +00:00
|
|
|
|
2018-06-14 08:11:24 +00:00
|
|
|
// State events
|
|
|
|
const (
|
|
|
|
StateAliases EventType = "m.room.aliases"
|
|
|
|
StateCanonicalAlias = "m.room.canonical_alias"
|
|
|
|
StateCreate = "m.room.create"
|
|
|
|
StateJoinRules = "m.room.join_rules"
|
|
|
|
StateMember = "m.room.member"
|
|
|
|
StatePowerLevels = "m.room.power_levels"
|
|
|
|
StateRoomName = "m.room.name"
|
|
|
|
StateTopic = "m.room.topic"
|
|
|
|
StateRoomAvatar = "m.room.avatar"
|
|
|
|
StatePinnedEvents = "m.room.pinned_events"
|
2018-06-13 22:41:05 +00:00
|
|
|
)
|
|
|
|
|
2018-06-14 08:11:24 +00:00
|
|
|
// Message events
|
|
|
|
const (
|
|
|
|
EventRedaction EventType = "m.room.redaction"
|
|
|
|
EventMessage = "m.room.message"
|
|
|
|
EventSticker = "m.sticker"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Msgtypes
|
|
|
|
const (
|
|
|
|
MsgText MessageType = "m.text"
|
|
|
|
MsgEmote = "m.emote"
|
|
|
|
MsgNotice = "m.notice"
|
|
|
|
MsgImage = "m.image"
|
|
|
|
MsgLocation = "m.location"
|
|
|
|
MsgVideo = "m.video"
|
|
|
|
MsgAudio = "m.audio"
|
|
|
|
)
|
|
|
|
|
|
|
|
const FormatHTML = "org.matrix.custom.html"
|
|
|
|
|
2018-06-14 17:21:44 +00:00
|
|
|
type EventHandler func(*Event) EventHandlerResult
|
|
|
|
type EventHandlerResult bool
|
|
|
|
|
|
|
|
const (
|
|
|
|
Continue EventHandlerResult = false
|
|
|
|
StopPropagation EventHandlerResult = true
|
|
|
|
)
|
2018-06-14 08:11:24 +00:00
|
|
|
|
|
|
|
type MatrixClient interface {
|
|
|
|
AddEventHandler(EventType, EventHandler)
|
2018-06-18 22:25:47 +00:00
|
|
|
AddCommandHandler(string, CommandHandler)
|
|
|
|
SetCommandSpec(*CommandSpec)
|
2018-06-14 10:36:53 +00:00
|
|
|
GetEvent(string, string) *Event
|
2018-06-14 08:11:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type EventFuncs interface {
|
2018-06-14 10:36:53 +00:00
|
|
|
MarkRead() error
|
2018-06-14 08:11:24 +00:00
|
|
|
Reply(string) (string, error)
|
|
|
|
ReplyContent(Content) (string, error)
|
|
|
|
SendMessage(string) (string, error)
|
|
|
|
SendContent(Content) (string, error)
|
|
|
|
SendRawEvent(EventType, interface{}) (string, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Event struct {
|
|
|
|
EventFuncs
|
|
|
|
|
2018-06-14 10:36:53 +00:00
|
|
|
StateKey string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
|
|
|
|
Sender string `json:"sender"` // The user ID of the sender of the event
|
|
|
|
Type EventType `json:"type"` // The event type
|
|
|
|
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
|
|
|
|
ID string `json:"event_id"` // The unique ID of this event
|
|
|
|
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
|
|
|
|
Content Content `json:"content"`
|
|
|
|
Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
|
|
|
|
Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver.
|
2018-06-14 08:11:24 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 22:25:47 +00:00
|
|
|
func (evt *Event) Equals(otherEvt *Event) bool {
|
|
|
|
return evt.StateKey == otherEvt.StateKey &&
|
|
|
|
evt.Sender == otherEvt.Sender &&
|
|
|
|
evt.Type == otherEvt.Type &&
|
|
|
|
evt.Timestamp == otherEvt.Timestamp &&
|
|
|
|
evt.ID == otherEvt.ID &&
|
|
|
|
evt.RoomID == otherEvt.RoomID &&
|
|
|
|
evt.Content.Equals(&otherEvt.Content) &&
|
|
|
|
evt.Redacts == otherEvt.Redacts &&
|
|
|
|
evt.Unsigned.Equals(&otherEvt.Unsigned)
|
|
|
|
}
|
|
|
|
|
2018-06-14 08:11:24 +00:00
|
|
|
type Unsigned struct {
|
2018-06-18 22:25:47 +00:00
|
|
|
PrevContent *Content `json:"prev_content,omitempty"`
|
|
|
|
PrevSender string `json:"prev_sender,omitempty"`
|
|
|
|
ReplacesState string `json:"replaces_state,omitempty"`
|
|
|
|
Age int64 `json:"age"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (unsigned Unsigned) Equals(otherUnsigned *Unsigned) bool {
|
|
|
|
return unsigned.PrevContent.Equals(otherUnsigned.PrevContent) &&
|
|
|
|
unsigned.PrevSender == otherUnsigned.PrevSender &&
|
|
|
|
unsigned.ReplacesState == otherUnsigned.ReplacesState &&
|
|
|
|
unsigned.Age == otherUnsigned.Age
|
2018-06-14 08:11:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Content struct {
|
|
|
|
Raw map[string]interface{} `json:"-"`
|
|
|
|
|
|
|
|
MsgType MessageType `json:"msgtype"`
|
|
|
|
Body string `json:"body"`
|
2018-06-14 10:36:53 +00:00
|
|
|
Format string `json:"format,omitempty"`
|
|
|
|
FormattedBody string `json:"formatted_body,omitempty"`
|
2018-06-14 08:11:24 +00:00
|
|
|
|
2018-06-18 22:25:47 +00:00
|
|
|
Info *FileInfo `json:"info,omitempty"`
|
|
|
|
URL string `json:"url,omitempty"`
|
2018-06-14 08:11:24 +00:00
|
|
|
|
2018-06-14 10:36:53 +00:00
|
|
|
Membership string `json:"membership,omitempty"`
|
2018-06-14 08:11:24 +00:00
|
|
|
|
2018-06-14 10:36:53 +00:00
|
|
|
RelatesTo RelatesTo `json:"m.relates_to,omitempty"`
|
2018-06-14 08:11:24 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 22:25:47 +00:00
|
|
|
func (content Content) Equals(otherContent *Content) bool {
|
|
|
|
return content.MsgType == otherContent.MsgType &&
|
|
|
|
content.Body == otherContent.Body &&
|
|
|
|
content.Format == otherContent.Format &&
|
|
|
|
content.FormattedBody == otherContent.FormattedBody &&
|
|
|
|
((content.Info != nil && content.Info.Equals(otherContent.Info)) || otherContent.Info == nil) &&
|
|
|
|
content.URL == otherContent.URL &&
|
|
|
|
content.Membership == otherContent.Membership &&
|
|
|
|
content.RelatesTo == otherContent.RelatesTo
|
|
|
|
}
|
|
|
|
|
2018-06-14 08:11:24 +00:00
|
|
|
type FileInfo struct {
|
2018-06-14 10:36:53 +00:00
|
|
|
MimeType string `json:"mimetype,omitempty"`
|
|
|
|
ThumbnailInfo *FileInfo `json:"thumbnail_info,omitempty"`
|
|
|
|
ThumbnailURL string `json:"thumbnail_url,omitempty"`
|
|
|
|
Height int `json:"h,omitempty"`
|
|
|
|
Width int `json:"w,omitempty"`
|
|
|
|
Size int `json:"size,omitempty"`
|
2018-06-14 08:11:24 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 22:25:47 +00:00
|
|
|
func (fi *FileInfo) Equals(otherFI *FileInfo) bool {
|
|
|
|
return fi.MimeType == otherFI.MimeType &&
|
|
|
|
fi.ThumbnailURL == otherFI.ThumbnailURL &&
|
|
|
|
fi.Height == otherFI.Height &&
|
|
|
|
fi.Width == otherFI.Width &&
|
|
|
|
fi.Size == otherFI.Size &&
|
|
|
|
((fi.ThumbnailInfo != nil && fi.ThumbnailInfo.Equals(otherFI.ThumbnailInfo)) || otherFI.ThumbnailInfo == nil)
|
|
|
|
}
|
|
|
|
|
2018-06-14 08:11:24 +00:00
|
|
|
type RelatesTo struct {
|
2018-06-14 10:36:53 +00:00
|
|
|
InReplyTo InReplyTo `json:"m.in_reply_to,omitempty"`
|
2018-06-14 08:11:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type InReplyTo struct {
|
|
|
|
EventID string `json:"event_id"`
|
|
|
|
// Not required, just for future-proofing
|
2018-06-14 10:36:53 +00:00
|
|
|
RoomID string `json:"room_id,omitempty"`
|
2018-06-13 22:41:05 +00:00
|
|
|
}
|