mirror of
https://github.com/aykhans/bsky-feedgen.git
synced 2025-07-17 21:34:00 +00:00
🦋
This commit is contained in:
250
pkg/manage/base.go
Normal file
250
pkg/manage/base.go
Normal file
@@ -0,0 +1,250 @@
|
||||
// This package was primarily developed using LLM models and should NOT be considered reliable.
|
||||
// The purpose of this package is to provide functionality for creating, updating, and deleting feed records on Bluesky, as no suitable tools were found for this purpose.
|
||||
// If a reliable tool becomes available that can perform these operations, this package will be deprecated and the discovered tool will be referenced in the project instead.
|
||||
|
||||
package manage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/aykhans/bsky-feedgen/pkg/utils"
|
||||
"github.com/bluesky-social/indigo/api/atproto"
|
||||
"github.com/bluesky-social/indigo/api/bsky"
|
||||
lexutil "github.com/bluesky-social/indigo/lex/util"
|
||||
"github.com/bluesky-social/indigo/xrpc"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultPDSHost = "https://bsky.social"
|
||||
)
|
||||
|
||||
func NewClient(pdsHost *string) *xrpc.Client {
|
||||
if pdsHost == nil {
|
||||
pdsHost = utils.ToPtr(DefaultPDSHost)
|
||||
}
|
||||
|
||||
return &xrpc.Client{
|
||||
Host: *pdsHost,
|
||||
}
|
||||
}
|
||||
|
||||
func NewClientWithAuth(ctx context.Context, client *xrpc.Client, identifier, password string) (*xrpc.Client, error) {
|
||||
if client == nil {
|
||||
client = NewClient(nil)
|
||||
}
|
||||
|
||||
auth, err := atproto.ServerCreateSession(ctx, client, &atproto.ServerCreateSession_Input{
|
||||
Identifier: identifier,
|
||||
Password: password,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create auth session: %v", err)
|
||||
}
|
||||
|
||||
client.Auth = &xrpc.AuthInfo{
|
||||
AccessJwt: auth.AccessJwt,
|
||||
RefreshJwt: auth.RefreshJwt,
|
||||
Did: auth.Did,
|
||||
Handle: auth.Handle,
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func uploadBlob(ctx context.Context, clientWithAuth *xrpc.Client, avatarPath string) (*atproto.RepoUploadBlob_Output, error) {
|
||||
if clientWithAuth == nil {
|
||||
return nil, fmt.Errorf("client can't be nil")
|
||||
}
|
||||
if clientWithAuth.Auth == nil {
|
||||
return nil, fmt.Errorf("client auth can't be nil")
|
||||
}
|
||||
|
||||
avatarFile, err := os.Open(avatarPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open avatar file: %v", err)
|
||||
}
|
||||
defer func() { _ = avatarFile.Close() }()
|
||||
|
||||
uploadResp, err := atproto.RepoUploadBlob(ctx, clientWithAuth, avatarFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload avatar: %v", err)
|
||||
}
|
||||
|
||||
return uploadResp, nil
|
||||
}
|
||||
|
||||
func GetFeedGenerator(ctx context.Context, clientWithAuth *xrpc.Client, keyName string) (*atproto.RepoGetRecord_Output, error) {
|
||||
if clientWithAuth == nil {
|
||||
return nil, fmt.Errorf("client can't be nil")
|
||||
}
|
||||
if clientWithAuth.Auth == nil {
|
||||
return nil, fmt.Errorf("client auth can't be nil")
|
||||
}
|
||||
|
||||
record, err := atproto.RepoGetRecord(
|
||||
ctx,
|
||||
clientWithAuth,
|
||||
"",
|
||||
"app.bsky.feed.generator",
|
||||
clientWithAuth.Auth.Did,
|
||||
keyName,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get axisting feed generator: %v", err)
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func CreateFeedGenerator(
|
||||
ctx context.Context,
|
||||
clientWithAuth *xrpc.Client,
|
||||
displayName string,
|
||||
description *string,
|
||||
avatarPath *string,
|
||||
did string,
|
||||
keyName string,
|
||||
) error {
|
||||
if clientWithAuth == nil {
|
||||
return fmt.Errorf("client can't be nil")
|
||||
}
|
||||
if clientWithAuth.Auth == nil {
|
||||
return fmt.Errorf("client auth can't be nil")
|
||||
}
|
||||
|
||||
var avatarBlob *lexutil.LexBlob
|
||||
if avatarPath != nil {
|
||||
uploadResp, err := uploadBlob(ctx, clientWithAuth, *avatarPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
avatarBlob = uploadResp.Blob
|
||||
}
|
||||
|
||||
record := bsky.FeedGenerator{
|
||||
DisplayName: displayName,
|
||||
Description: description,
|
||||
Avatar: avatarBlob,
|
||||
Did: did,
|
||||
CreatedAt: time.Now().Format(time.RFC3339Nano),
|
||||
}
|
||||
|
||||
wrappedRecord := &lexutil.LexiconTypeDecoder{
|
||||
Val: &record,
|
||||
}
|
||||
|
||||
_, err := atproto.RepoCreateRecord(ctx, clientWithAuth, &atproto.RepoCreateRecord_Input{
|
||||
Collection: "app.bsky.feed.generator",
|
||||
Repo: clientWithAuth.Auth.Did, // Your DID (the one creating the record)
|
||||
Record: wrappedRecord,
|
||||
Rkey: &keyName,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create feed generator: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateFeedGenerator(
|
||||
ctx context.Context,
|
||||
clientWithAuth *xrpc.Client,
|
||||
displayName *string,
|
||||
description *string,
|
||||
avatarPath *string,
|
||||
did *string,
|
||||
keyName string,
|
||||
) error {
|
||||
if clientWithAuth == nil {
|
||||
return fmt.Errorf("client can't be nil")
|
||||
}
|
||||
if clientWithAuth.Auth == nil {
|
||||
return fmt.Errorf("client auth can't be nil")
|
||||
}
|
||||
|
||||
existingRecord, err := GetFeedGenerator(ctx, clientWithAuth, keyName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get axisting feed generator: %v", err)
|
||||
}
|
||||
|
||||
if existingRecord != nil && existingRecord.Value != nil {
|
||||
if existingFeedgen, ok := existingRecord.Value.Val.(*bsky.FeedGenerator); ok {
|
||||
if avatarPath != nil {
|
||||
uploadResp, err := uploadBlob(ctx, clientWithAuth, *avatarPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingFeedgen.Avatar = uploadResp.Blob
|
||||
}
|
||||
|
||||
if displayName != nil {
|
||||
existingFeedgen.DisplayName = *displayName
|
||||
}
|
||||
|
||||
if description != nil {
|
||||
existingFeedgen.Description = description
|
||||
}
|
||||
|
||||
if did != nil {
|
||||
existingFeedgen.Did = *did
|
||||
}
|
||||
|
||||
wrappedExistingFeedgen := &lexutil.LexiconTypeDecoder{
|
||||
Val: &bsky.FeedGenerator{
|
||||
DisplayName: existingFeedgen.DisplayName,
|
||||
Description: existingFeedgen.Description,
|
||||
Did: existingFeedgen.Did,
|
||||
Avatar: existingFeedgen.Avatar,
|
||||
CreatedAt: existingFeedgen.CreatedAt,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := atproto.RepoPutRecord(ctx, clientWithAuth, &atproto.RepoPutRecord_Input{
|
||||
Collection: "app.bsky.feed.generator",
|
||||
Repo: clientWithAuth.Auth.Did, // Your DID
|
||||
Rkey: keyName, // The Rkey of the record to update
|
||||
Record: wrappedExistingFeedgen,
|
||||
SwapRecord: existingRecord.Cid,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update feed generator: %v", err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("feed generator not found")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteFeedGenerator(
|
||||
ctx context.Context,
|
||||
clientWithAuth *xrpc.Client,
|
||||
keyName string,
|
||||
) error {
|
||||
if clientWithAuth == nil {
|
||||
return fmt.Errorf("client can't be nil")
|
||||
}
|
||||
if clientWithAuth.Auth == nil {
|
||||
return fmt.Errorf("client auth can't be nil")
|
||||
}
|
||||
|
||||
f, err := atproto.RepoDeleteRecord(ctx, clientWithAuth, &atproto.RepoDeleteRecord_Input{
|
||||
Collection: "app.bsky.feed.generator",
|
||||
Repo: clientWithAuth.Auth.Did,
|
||||
Rkey: keyName,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete feed generator: %v", err)
|
||||
}
|
||||
if f.Commit == nil {
|
||||
return fmt.Errorf("feed generator not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user