227 lines
6 KiB
Go
227 lines
6 KiB
Go
package bsky
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/log"
|
|
"github.com/dghubble/sling"
|
|
)
|
|
|
|
type BlueskyConfig struct {
|
|
PDSURL string `json:"pds-url"`
|
|
Repo string `json:"repo"`
|
|
Handle string `json:"handle"`
|
|
DID string `json:"did"`
|
|
AppPassword string `json:"app-password"`
|
|
AccessJWT string `json:"access-jwt"`
|
|
RefreshJWT string `json:"refresh-jwt"`
|
|
Cursor int64 `json:"cursor"`
|
|
}
|
|
|
|
type DIDResponse struct {
|
|
Context []string `json:"@context"`
|
|
ID string `json:"id"`
|
|
AlsoKnownAs []string `json:"alsoKnownAs"`
|
|
VerificationMethod []struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
Controller string `json:"controller"`
|
|
PublicKeyMultibase string `json:"publicKeyMultibase"`
|
|
} `json:"verificationMethod"`
|
|
Service []struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
ServiceEndpoint string `json:"serviceEndpoint"`
|
|
} `json:"service"`
|
|
}
|
|
|
|
type BSkySessionResponse struct {
|
|
AccessJWT string `json:"accessJwt,omitempty"`
|
|
RefreshJWT string `json:"refreshJwt,omitempty"`
|
|
Handle string `json:"handle"`
|
|
DID string `json:"did"`
|
|
Error string `json:"error,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
}
|
|
|
|
type CommitResponse struct {
|
|
URI string `json:"uri"`
|
|
Cid string `json:"cid"`
|
|
Commit struct {
|
|
Cid string `json:"cid"`
|
|
Rev string `json:"rev"`
|
|
} `json:"commit"`
|
|
Error string `json:"error"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (c *CommitResponse) GetRKey() string {
|
|
s := strings.SplitN(c.URI, "/", 5)
|
|
if len(s) == 5 {
|
|
return s[4]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
type Link struct {
|
|
Cid string `json:"cid"`
|
|
URI string `json:"uri"`
|
|
}
|
|
|
|
type Bluesky struct {
|
|
Cfg *BlueskyConfig
|
|
HttpClient *http.Client
|
|
Logger *log.Logger
|
|
sling *sling.Sling
|
|
}
|
|
|
|
func (bluesky *Bluesky) CreateSession(cfg *BlueskyConfig) error {
|
|
body := struct {
|
|
Identifier string `json:"identifier"`
|
|
Password string `json:"password"`
|
|
}{
|
|
Identifier: cfg.Handle,
|
|
Password: cfg.AppPassword,
|
|
}
|
|
resp := new(BSkySessionResponse)
|
|
|
|
bluesky.sling.New().Post("/xrpc/com.atproto.server.createSession").BodyJSON(body).ReceiveSuccess(resp)
|
|
if resp.AccessJWT != "" {
|
|
cfg.AccessJWT = resp.AccessJWT
|
|
cfg.RefreshJWT = resp.RefreshJWT
|
|
return nil
|
|
}
|
|
|
|
bluesky.sling.New().Set("Authorization", fmt.Sprintf("Bearer %s", bluesky.Cfg.AccessJWT))
|
|
return errors.New("unable to authenticate, check handle/password")
|
|
}
|
|
|
|
func (bluesky *Bluesky) RefreshSession() error {
|
|
resp := new(BSkySessionResponse)
|
|
|
|
bluesky.sling.New().Set("Authorization", fmt.Sprintf("Bearer %s", bluesky.Cfg.RefreshJWT)).
|
|
Post("/xrpc/com.atproto.server.refreshSession").Receive(resp, resp)
|
|
if resp.AccessJWT != "" {
|
|
bluesky.Cfg.AccessJWT = resp.AccessJWT
|
|
bluesky.Cfg.RefreshJWT = resp.RefreshJWT
|
|
PersistAuthSession(bluesky.Cfg)
|
|
bluesky.sling.Set("Authorization", fmt.Sprintf("Bearer %s", bluesky.Cfg.AccessJWT))
|
|
return nil
|
|
}
|
|
|
|
return bluesky.CreateSession(bluesky.Cfg)
|
|
}
|
|
|
|
func (bluesky *Bluesky) CheckSessionValid() {
|
|
resp := new(BSkySessionResponse)
|
|
|
|
bluesky.sling.New().Set("Authorization", fmt.Sprintf("Bearer %s", bluesky.Cfg.AccessJWT)).
|
|
Get("/xrpc/app.bsky.actor.getProfile").Receive(resp, resp)
|
|
if resp.Error == "ExpiredToken" {
|
|
bluesky.RefreshSession()
|
|
}
|
|
}
|
|
|
|
type TelegramRecord struct {
|
|
ChannelID int64 `json:"channel_id"`
|
|
MessageID int `json:"message_id"`
|
|
Link *Link `json:"link"`
|
|
Error string `json:"error"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (bluesky *Bluesky) CommitTelegramResponse(data *TelegramRecord, rkey string) *CommitResponse {
|
|
bluesky.CheckSessionValid()
|
|
|
|
resp := new(CommitResponse)
|
|
|
|
record := struct {
|
|
Repo string `json:"repo"`
|
|
Collection string `json:"collection"`
|
|
RKey string `json:"rkey"`
|
|
Record TelegramRecord `json:"record"`
|
|
}{
|
|
Repo: bluesky.Cfg.DID,
|
|
Collection: "blue.zio.bsky2tg.post",
|
|
RKey: rkey,
|
|
Record: TelegramRecord{
|
|
ChannelID: data.ChannelID,
|
|
MessageID: data.MessageID,
|
|
Link: &Link{
|
|
Cid: data.Link.Cid,
|
|
URI: data.Link.URI,
|
|
},
|
|
},
|
|
}
|
|
|
|
bluesky.sling.New().Set("Authorization", fmt.Sprintf("Bearer %s", bluesky.Cfg.AccessJWT)).
|
|
Post("/xrpc/com.atproto.repo.createRecord").BodyJSON(&record).Receive(resp, resp)
|
|
return resp
|
|
}
|
|
|
|
func (bluesky *Bluesky) GetTelegramData(rkey string) (*TelegramRecord, string) {
|
|
resp := &struct {
|
|
Value *TelegramRecord `json:"value"`
|
|
URI string `json:"uri"`
|
|
Cid string `json:"cid"`
|
|
Error string `json:"error"`
|
|
Message string `json:"message"`
|
|
}{}
|
|
|
|
params := struct {
|
|
Repo string `url:"repo"`
|
|
Collection string `url:"collection"`
|
|
RKey string `url:"rkey"`
|
|
}{
|
|
Repo: bluesky.Cfg.DID,
|
|
Collection: "blue.zio.bsky2tg.post",
|
|
RKey: rkey,
|
|
}
|
|
|
|
bluesky.sling.New().Get("/xrpc/com.atproto.repo.getRecord").QueryStruct(¶ms).Receive(resp, resp)
|
|
return resp.Value, resp.Message
|
|
}
|
|
|
|
func (bluesky *Bluesky) GetPost(uri string) *Post {
|
|
bluesky.CheckSessionValid()
|
|
var post = struct {
|
|
URI string `json:"uri"`
|
|
CID string `json:"cid"`
|
|
Value *Post `json:"value"`
|
|
}{}
|
|
|
|
args := strings.SplitN(uri, "/", 5)
|
|
params := struct {
|
|
RKey string `url:"rkey"`
|
|
Repo string `url:"repo"`
|
|
Collection string `url:"collection"`
|
|
}{
|
|
RKey: args[4],
|
|
Repo: args[2],
|
|
Collection: args[3],
|
|
}
|
|
bluesky.sling.New().Get("/xrpc/com.atproto.repo.getRecord").QueryStruct(params).ReceiveSuccess(&post)
|
|
|
|
return post.Value
|
|
}
|
|
|
|
func (bluesky *Bluesky) DeleteRecord(args []string) *CommitResponse {
|
|
bluesky.CheckSessionValid()
|
|
|
|
resp := new(CommitResponse)
|
|
params := struct {
|
|
RKey string `json:"rkey"`
|
|
Repo string `json:"repo"`
|
|
Collection string `json:"collection"`
|
|
}{
|
|
RKey: args[0],
|
|
Repo: args[1],
|
|
Collection: args[2],
|
|
}
|
|
bluesky.sling.New().Set("Authorization", fmt.Sprintf("Bearer %s", bluesky.Cfg.AccessJWT)).
|
|
Post("/xrpc/com.atproto.repo.deleteRecord").BodyJSON(params).Receive(resp, resp)
|
|
return resp
|
|
}
|