func (s *Store) AddMessage(m *Message) error {
	if err := s.db.Update(func(tx *bolt.Tx) error {
		r, err := s.repository(tx, m.RepositoryID)
		if err != nil {
			return err
		}
		if r == nil {
			repo, err := s.RemoteStore.Repository(m.RepositoryID)
			if err != nil {
				return fmt.Errorf("remote: %s", err)
			} else if repo == nil {
				return ErrRepositoryNotFound
			}
			r = encodeRepository(repo)
		}
		for _, msg := range r.GetMessages() {
			if msg.GetID() == m.ID {
				return errDuplicateMessage
			}
		}
		r.Messages = append(r.Messages, encodeMessage(m))
		if err := s.saveRepository(tx, r); err != nil {
			return err
		}
		return nil
	}); err == errDuplicateMessage {
		return nil  
	} else if err != nil {
		return err
	}
	return nil
}
AddMessage adds a message related to a repository.
Retrieves repository data from the remote store, if needed.
package scuttlebutt
import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
	"github.com/benbjohnson/scuttlebutt/internal"
	"github.com/boltdb/bolt"
	"github.com/gogo/protobuf/proto"
)
var (
	ErrRepositoryNotFound = errors.New("repository not found")
)
type Store struct {
	path string
	db   *bolt.DB
	RemoteStore interface {
		Repository(id string) (*Repository, error)
	}
}
func NewStore(path string) *Store {
	return &Store{
		path: path,
	}
}
func (s *Store) Path() string { return s.path }
func (s *Store) Open() error {
	db, err := bolt.Open(s.path, 0666, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		return err
	}
	s.db = db
	if err := s.db.Update(func(tx *bolt.Tx) error {
		tx.CreateBucketIfNotExists([]byte("repositories"))
		tx.CreateBucketIfNotExists([]byte("meta"))
		return nil
	}); err != nil {
		s.Close()
		return err
	}
	return nil
}
func (s *Store) Close() error {
	if s.db != nil {
		s.db.Close()
	}
	return nil
}
func (s *Store) Ping() error {
	return s.db.View(func(tx *bolt.Tx) error { return nil })
}