bluebubbles

package
v0.0.0-...-892f056 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 27, 2024 License: AGPL-3.0 Imports: 22 Imported by: 0

README

BlueBubbles iMessage Bridge Interface

Prerequisties

  1. Ensure you have a Mac System with the Messages App working
  2. Install BlueBubbles Server on it.
    1. Accept all the defaults while installing the server
    2. Ignore Private API during the initial install, it can be enabled later.
  3. (Optional) Enable BlueBubbles Private API
    1. Note: This requires disabling System Integrity Protection (SIP), which is not a wise thing to do on a Mac you use out in the world on a regular basis.
    2. Not enabling Private API makes the following features unavailable:
      1. "Tap Backs": aka Emoji Reactions
      2. Please let us know if there's more you find that doesn't work...
  4. Familiarize yourself with the BlueBubbles API

Using the Bridge (Start with the Prerequisties!)

  1. Download the most recent bbctl from the most recent GitHub Actions build
  2. Move it somewhere on your system that is in your $PATH, and chmod +x bbctl
  3. Run bbctl login to login to your Beeper Account
  4. Download the most recent mautrix-imessage from the bluebubbles branch
  5. Run bbctl run --custom-startup-command ~/PATH/TO/EXTRACTED/ARTIFACT/mautrix-imessage --param 'imessage_platform=bluebubbles' sh-imessage
  6. When prompted, provide your Server Address listed in the BlueBubbles server UI
  7. When prompted, provide your Server Password listed in the BlueBubbles server UI

Development

  1. Download the most recent bbctl from the most recent GitHub Actions build
  2. Move it somewhere on your system that is in your $PATH, and chmod +x bbctl
  3. Run bbctl login to login to your Beeper Account
  4. Install pre-commit
Running Locally
  1. Clone this repository: git clone git@github.com:mautrix/imessage.git
  2. cd into imessage
  3. Setup pre-commit hooks: pre-commit install
  4. Switch to the bluebubbles branch: git checkout bluebubbles
    1. This is temporary while we're developing this feature.
    2. You'll know you're in the right spot if you can see this README in your local code.
  5. Run the following command to launch the bridge in development mode:
bbctl run --local-dev --param 'bluebubbles_url=<YOUR BLUEBUBBLES URL>' --param 'bluebubbles_password=<YOUR BLUEBUBBLES PASSWORD>' --param 'imessage_platform=bluebubbles' sh-imessage

NOTE: Double check that the config.yaml that was automatically generated has the correct values set for bluebubbles_url and bluebubbles_password, as sometimes bbctl run doesn't copy the param properly.

Troubleshooting

If you encounter strange errors, try resetting your environment:

bbctl delete sh-imessage
rm ./mautrix-imessage ./mautrix-imessage.db config.yaml

Then start from the bbctl run ... command, and ensure that bluebubbles_url and bluebubbles_password are set in your config.

Contributing
  1. Find an open issue or bug
  2. Create and switch a fork of this repository and branch
  3. Hack away
  4. Submit a PR
Join us the community chat
  1. In the Beeper Desktop App, click the Settings Gear (Cog) at the top
  2. Add a server: maunium.net
  3. Find the iMessage Room: #imessage:maunium.net
  4. Say hello!

Documentation

Index

Constants

View Source
const (
	// Ref: https://github.com/BlueBubblesApp/bluebubbles-server/blob/master/packages/server/src/server/events.ts
	NewMessage            string = "new-message"
	MessageSendError      string = "message-send-error"
	MessageUpdated        string = "updated-message"
	ParticipantRemoved    string = "participant-removed"
	ParticipantAdded      string = "participant-added"
	ParticipantLeft       string = "participant-left"
	GroupIconChanged      string = "group-icon-changed"
	GroupIconRemoved      string = "group-icon-removed"
	ChatReadStatusChanged string = "chat-read-status-changed"
	TypingIndicator       string = "typing-indicator"
	GroupNameChanged      string = "group-name-change"
	IMessageAliasRemoved  string = "imessage-alias-removed"
)

Variables

View Source
var ErrNotImplemented = errors.New("not implemented")

Functions

func NewBlueBubblesConnector

func NewBlueBubblesConnector(bridge imessage.Bridge) (imessage.API, error)

func RandString

func RandString(n int) string

Types

type Attachment

type Attachment struct {
	OriginalRowID  int    `json:"originalROWID,omitempty"`
	GUID           string `json:"guid,omitempty"`
	UTI            string `json:"uti,omitempty"`
	MimeType       string `json:"mimeType,omitempty"`
	TransferName   string `json:"transferName,omitempty"`
	TotalBytes     int64  `json:"totalBytes,omitempty"`
	TransferState  int    `json:"transferState,omitempty"`
	IsOutgoing     bool   `json:"isOutgoing,omitempty"`
	HideAttachment bool   `json:"hideAttachment,omitempty"`
	IsSticker      bool   `json:"isSticker,omitempty"`
	OriginalGUID   string `json:"originalGuid,omitempty"`
	HasLivePhoto   bool   `json:"hasLivePhoto,omitempty"`
	Height         int64  `json:"height,omitempty"`
	Width          int64  `json:"width,omitempty"`
	Metadata       any    `json:"metadata,omitempty"`
}

type AttachmentResponse

type AttachmentResponse struct {
	Status  int64      `json:"status"`
	Message string     `json:"message"`
	Data    Attachment `json:"data"`
}

type Chat

type Chat struct {
	// TODO How to get timestamp
	GUID           string           `json:"guid"`
	ChatIdentifier string           `json:"chatIdentifier"`
	GroupID        string           `json:"groupId,omitempty"`
	DisplayName    string           `json:"displayName"`
	Participants   []Participant    `json:"participants"`
	LastMessage    *Message         `json:"lastMessage,omitempty"`
	Properties     []ChatProperties `json:"properties,omitempty"`
}

type ChatProperties

type ChatProperties struct {
	GroupPhotoGUID *string `json:"groupPhotoGuid,omitempty"`
}

type ChatQueryRequest

type ChatQueryRequest struct {
	// TODO Other Fields
	Limit  int64           `json:"limit"`
	Offset int64           `json:"offset"`
	With   []ChatQueryWith `json:"with"`
	Sort   ChatQuerySort   `json:"sort"`
}

type ChatQueryResponse

type ChatQueryResponse struct {
	Status   int64        `json:"status"`
	Message  string       `json:"message"`
	Data     []Chat       `json:"data"`
	Metadata PageMetadata `json:"metadata"`
}

type ChatQuerySort

type ChatQuerySort string
const (
	QuerySortLastMessage ChatQuerySort = "lastmessage"
)

type ChatQueryWith

type ChatQueryWith string
const (
	MessageQueryWithChat             ChatQueryWith = "chat"
	MessageQueryWithChatParticipants ChatQueryWith = "chat.participants"
	MessageQueryWithAttachment       ChatQueryWith = "attachment"
	MessageQueryWithHandle           ChatQueryWith = "handle"
	MessageQueryWithSMS              ChatQueryWith = "sms"
	MessageQueryWithAttributeBody    ChatQueryWith = "message.attributedBody"
	MessageQueryWithMessageSummary   ChatQueryWith = "message.messageSummaryInfo"
	MessageQueryWithPayloadData      ChatQueryWith = "message.payloadData"
)
const (
	ChatQueryWithSMS         ChatQueryWith = "sms"
	ChatQueryWithLastMessage ChatQueryWith = "lastMessage"
)

type ChatResponse

type ChatResponse struct {
	Status  int64  `json:"status"`
	Message string `json:"message"`
	Data    *Chat  `json:"data,omitempty"`
}

type Contact

type Contact struct {
	PhoneNumbers []PhoneNumber `json:"phoneNumbers,omitempty"`
	Emails       []Email       `json:"emails,omitempty"`
	FirstName    string        `json:"firstName,omitempty"`
	LastName     string        `json:"lastName,omitempty"`
	DisplayName  string        `json:"displayName,omitempty"`
	Nickname     string        `json:"nickname,omitempty"`
	Birthday     string        `json:"birthday,omitempty"`
	Avatar       *string       `json:"avatar,omitempty"`
	SourceType   string        `json:"sourceType,omitempty"`
	// DEVNOTE this field is a string unless importing from a vCard
	ID any `json:"id,omitempty"`
}

type ContactQueryRequest

type ContactQueryRequest struct {
	Addresses []string `json:"addresses"`
}

type ContactResponse

type ContactResponse struct {
	Status  int64     `json:"status"`
	Message string    `json:"message"`
	Data    []Contact `json:"data"`
}

type EditMessage

type EditMessage struct {
	EditedMessage                  string `json:"editedMessage"`
	BackwwardsCompatibilityMessage string `json:"backwardsCompatibilityMessage"`
	PartIndex                      int    `json:"partIndex"`
}

type EditMessageResponse

type EditMessageResponse struct {
	Status  int64   `json:"status"`
	Message string  `json:"message"`
	Data    Message `json:"data,omitempty"`
	Error   any     `json:"error"`
}

type Email

type Email struct {
	Address string `json:"address,omitempty"`
	ID      any    `json:"id,omitempty"`
}

type GetMessagesResponse

type GetMessagesResponse struct {
	Status  int64     `json:"status"`
	Message string    `json:"message"`
	Data    []Message `json:"data"`
	Error   any       `json:"error,omitempty"`
}

type Handle

type Handle struct {
	Address           string `json:"address,omitempty"`
	Country           string `json:"country,omitempty"`
	OriginalROWID     int    `json:"originalROWID,omitempty"`
	Service           string `json:"service,omitempty"`
	UncanonicalizedID any    `json:"uncanonicalizedId,omitempty"`
}

type Message

type Message struct {
	AssociatedMessageGUID     string       `json:"associatedMessageGuid,omitempty"`
	AssociatedMessageType     string       `json:"associatedMessageType,omitempty"`
	Attachments               []Attachment `json:"attachments,omitempty"`
	AttributedBody            []any        `json:"attributedBody,omitempty"`
	BalloonBundleID           any          `json:"balloonBundleId,omitempty"`
	Chats                     []Chat       `json:"chats,omitempty"`
	DateCreated               int64        `json:"dateCreated,omitempty"`
	DateDelivered             int64        `json:"dateDelivered,omitempty"`
	DateEdited                int64        `json:"dateEdited,omitempty"`
	DateRead                  int64        `json:"dateRead,omitempty"`
	DateRetracted             int64        `json:"dateRetracted,omitempty"`
	Error                     int          `json:"error,omitempty"`
	ExpressiveSendStyleID     any          `json:"expressiveSendStyleId,omitempty"`
	GroupActionType           int          `json:"groupActionType,omitempty"`
	GroupTitle                string       `json:"groupTitle,omitempty"`
	GUID                      string       `json:"guid,omitempty"`
	Handle                    Handle       `json:"handle,omitempty"`
	HandleID                  int          `json:"handleId,omitempty"`
	HasDDResults              bool         `json:"hasDdResults,omitempty"`
	HasPayloadData            bool         `json:"hasPayloadData,omitempty"`
	IsArchived                bool         `json:"isArchived,omitempty"`
	IsAudioMessage            bool         `json:"isAudioMessage,omitempty"`
	IsAutoReply               bool         `json:"isAutoReply,omitempty"`
	IsCorrupt                 bool         `json:"isCorrupt,omitempty"`
	IsDelayed                 bool         `json:"isDelayed,omitempty"`
	IsExpired                 bool         `json:"isExpired,omitempty"`
	IsForward                 bool         `json:"isForward,omitempty"`
	IsFromMe                  bool         `json:"isFromMe,omitempty"`
	IsServiceMessage          bool         `json:"isServiceMessage,omitempty"`
	IsSpam                    bool         `json:"isSpam,omitempty"`
	IsSystemMessage           bool         `json:"isSystemMessage,omitempty"`
	ItemType                  int          `json:"itemType,omitempty"`
	MessageSummaryInfo        any          `json:"messageSummaryInfo,omitempty"`
	OriginalROWID             int          `json:"originalROWID,omitempty"`
	OtherHandle               int          `json:"otherHandle,omitempty"`
	PartCount                 int          `json:"partCount,omitempty"`
	PayloadData               any          `json:"payloadData,omitempty"`
	ReplyToGUID               string       `json:"replyToGuid,omitempty"`
	ShareDirection            int          `json:"shareDirection,omitempty"`
	ShareStatus               int          `json:"shareStatus,omitempty"`
	Subject                   string       `json:"subject,omitempty"`
	Text                      string       `json:"text,omitempty"`
	ThreadOriginatorGUID      string       `json:"threadOriginatorGuid,omitempty"`
	ThreadOriginatorPart      string       `json:"threadOriginatorPart,omitempty"`
	TimeExpressiveSendStyleID any          `json:"timeExpressiveSendStyleId,omitempty"`
	WasDeliveredQuietly       bool         `json:"wasDeliveredQuietly,omitempty"`
}

type MessageQueryRequest

type MessageQueryRequest struct {
	// TODO Other Fields
	ChatGUID string             `json:"chatGuid"`
	Limit    int                `json:"limit"`
	Max      *int               `json:"max"`
	Offset   int                `json:"offset"`
	With     []MessageQueryWith `json:"with"`
	Sort     MessageQuerySort   `json:"sort"`
	Before   *int64             `json:"before,omitempty"`
	After    *int64             `json:"after,omitempty"`
}

type MessageQueryResponse

type MessageQueryResponse struct {
	Status   int64        `json:"status"`
	Message  string       `json:"message"`
	Data     []Message    `json:"data"`
	Metadata PageMetadata `json:"metadata"`
}

type MessageQuerySort

type MessageQuerySort string
const (
	MessageQuerySortAsc  MessageQuerySort = "ASC"
	MessageQuerySortDesc MessageQuerySort = "DESC"
)

type MessageQueryWith

type MessageQueryWith string

type MessageReadResponse

type MessageReadResponse struct {
	ChatGUID string `json:"chatGuid"`
	Read     bool   `json:"read"`
}

type MessageResponse

type MessageResponse struct {
	Status  int64   `json:"status"`
	Message string  `json:"message"`
	Data    Message `json:"data"`
	Error   any     `json:"error,omitempty"`
}

type PageMetadata

type PageMetadata struct {
	Count  int64 `json:"count"`
	Total  int64 `json:"total"`
	Offset int64 `json:"offset"`
	Limit  int64 `json:"limit"`
}

type Participant

type Participant struct {
	Address string `json:"address"`
}

type PhoneNumber

type PhoneNumber struct {
	Address string `json:"address,omitempty"`
	ID      any    `json:"id,omitempty"`
}

type ReadReceiptResponse

type ReadReceiptResponse struct {
	Status  int64  `json:"status"`
	Message string `json:"message"`
	Error   any    `json:"error"`
}

type ResolveIdentifierResponse

type ResolveIdentifierResponse struct {
	Status  int64  `json:"status"`
	Message string `json:"message"`
	Data    Handle `json:"data"`
}

type SendReactionRequest

type SendReactionRequest struct {
	ChatGUID            string `json:"chatGuid"`
	Reaction            string `json:"reaction"`
	SelectedMessageGUID string `json:"selectedMessageGuid"`
	PartIndex           int    `json:"partIndex"`
}

type SendReactionResponse

type SendReactionResponse struct {
	Status  int64   `json:"status"`
	Message string  `json:"message"`
	Data    Message `json:"data,omitempty"`
	Error   any     `json:"error"`
}

type SendTextRequest

type SendTextRequest struct {
	ChatGUID            string `json:"chatGuid"`
	TempGUID            string `json:"tempGuid"`
	Method              string `json:"method"`
	Message             string `json:"message"`
	EffectID            string `json:"effectId,omitempty"`
	Subject             string `json:"subject,omitempty"`
	SelectedMessageGUID string `json:"selectedMessageGuid,omitempty"`
	PartIndex           int    `json:"partIndex,omitempty"`
}

type SendTextResponse

type SendTextResponse struct {
	Status  int64   `json:"status"`
	Message string  `json:"message"`
	Data    Message `json:"data,omitempty"`
	Error   any     `json:"error,omitempty"`
}

type ServerInfo

type ServerInfo struct {
	PrivateAPI bool `json:"private_api"`
}

type ServerInfoResponse

type ServerInfoResponse struct {
	Status  int64      `json:"status"`
	Message string     `json:"message"`
	Data    ServerInfo `json:"data"`
}

type TypingNotification

type TypingNotification struct {
	Display bool   `json:"display"`
	GUID    string `json:"guid"`
}

type TypingResponse

type TypingResponse struct {
	Status  int64  `json:"status"`
	Message string `json:"message"`
	Error   any    `json:"error"`
}

type UnsendMessage

type UnsendMessage struct {
	PartIndex int `json:"partIndex"`
}

type UnsendMessageResponse

type UnsendMessageResponse struct {
	Status  int64   `json:"status"`
	Message string  `json:"message"`
	Data    Message `json:"data,omitempty"`
	Error   any     `json:"error"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL