add files

This commit is contained in:
Astra 2025-06-05 07:22:57 +01:00
commit 04e29ed525
7 changed files with 1337 additions and 0 deletions

227
bsky/bluesky.go Normal file
View file

@ -0,0 +1,227 @@
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(&params).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 `url:"rkey"`
Repo string `url:"repo"`
Collection string `url:"collection"`
}{
RKey: args[0],
Repo: args[1],
Collection: args[2],
}
bluesky.sling.New().Set("Authorization", fmt.Sprintf("Bearer %s", bluesky.Cfg.AccessJWT)).
Get("/xrpc/com.atproto.repo.deleteRecord").QueryStruct(params).ReceiveSuccess(resp)
return resp
}