Skip to content

Commit 8ea19b8

Browse files
authored
Merge pull request #13 from wcatz/duckx
feat(twitter posts): long live duckBot
2 parents fc61115 + d1e8408 commit 8ea19b8

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/cardano-community/koios-go-client/v3 v3.1.3
1111
github.com/cenkalti/backoff/v4 v4.3.0
1212
github.com/gorilla/websocket v1.5.3
13+
github.com/michimani/gotwi v0.18.1
1314
github.com/spf13/viper v1.21.0
1415
gopkg.in/tucnak/telebot.v2 v2.5.0
1516
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
120120
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
121121
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
122122
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
123+
github.com/michimani/gotwi v0.18.1 h1:Tp7uia9qby8I0AXk9oDZRqaCPg31qyQ7NkeyiGDCDaE=
124+
github.com/michimani/gotwi v0.18.1/go.mod h1:yz1cyV/30Uy/KGQyN8BVfXFPt/63Imzonykny8/SMi0=
123125
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
124126
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
125127
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=

main.go

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9+
"io"
910
"log"
1011
"net/http"
1112
"os"
@@ -22,6 +23,9 @@ import (
2223
koios "github.com/cardano-community/koios-go-client/v3"
2324
"github.com/cenkalti/backoff/v4"
2425
"github.com/gorilla/websocket"
26+
"github.com/michimani/gotwi"
27+
"github.com/michimani/gotwi/tweet/managetweet"
28+
"github.com/michimani/gotwi/tweet/managetweet/types"
2529
"github.com/spf13/viper"
2630
telebot "gopkg.in/tucnak/telebot.v2"
2731
)
@@ -69,6 +73,9 @@ type Indexer struct {
6973
epoch int
7074
networkMagic int
7175
wg sync.WaitGroup
76+
// Twitter fields
77+
twitterClient *gotwi.Client
78+
twitterEnabled bool
7279
}
7380

7481
type BlockEvent struct {
@@ -138,6 +145,32 @@ func (i *Indexer) Start() error {
138145
log.Fatalf("failed to start bot: %s", err)
139146
}
140147

148+
// Initialize Twitter client if credentials are provided
149+
twitterAPIKey := os.Getenv("TWITTER_API_KEY")
150+
twitterAPISecret := os.Getenv("TWITTER_API_KEY_SECRET")
151+
twitterAccessToken := os.Getenv("TWITTER_ACCESS_TOKEN")
152+
twitterAccessTokenSecret := os.Getenv("TWITTER_ACCESS_TOKEN_SECRET")
153+
154+
if twitterAPIKey != "" && twitterAPISecret != "" &&
155+
twitterAccessToken != "" && twitterAccessTokenSecret != "" {
156+
in := &gotwi.NewClientInput{
157+
AuthenticationMethod: gotwi.AuthenMethodOAuth1UserContext,
158+
OAuthToken: twitterAccessToken,
159+
OAuthTokenSecret: twitterAccessTokenSecret,
160+
APIKey: twitterAPIKey,
161+
APIKeySecret: twitterAPISecret,
162+
}
163+
i.twitterClient, err = gotwi.NewClient(in)
164+
if err != nil {
165+
log.Printf("failed to initialize Twitter client: %s", err)
166+
} else {
167+
i.twitterEnabled = true
168+
log.Println("Twitter client initialized successfully")
169+
}
170+
} else {
171+
log.Println("Twitter credentials not provided, Twitter notifications disabled")
172+
}
173+
141174
/// Initialize the kois client based on networkMagic number
142175
if i.networkMagic == 1 {
143176
i.koios, e = koios.New(
@@ -357,16 +390,37 @@ func (i *Indexer) handleEvent(event event.Event) error {
357390
timeDiffString, i.epochBlocks, i.totalBlocks,
358391
blockEvent.Context.BlockNumber, blockEvent.Payload.BlockHash)
359392

393+
// Get duck image URL for both Telegram and Twitter
394+
duckImageURL := getDuckImage()
395+
360396
// Send the message to the appropriate channel
361397
channelID, err := strconv.ParseInt(i.telegramChannel, 10, 64)
362398
if err != nil {
363399
log.Fatalf("failed to parse telegram channel ID: %s", err)
364400
}
365-
photo := &telebot.Photo{File: telebot.FromURL(getDuckImage()), Caption: msg}
401+
photo := &telebot.Photo{File: telebot.FromURL(duckImageURL), Caption: msg}
366402
_, err = i.bot.Send(&telebot.Chat{ID: channelID}, photo)
367403
if err != nil {
368404
log.Printf("failed to send Telegram message: %s", err)
369405
}
406+
407+
// Send tweet if Twitter is enabled (shorter format for 280 char limit)
408+
if i.twitterEnabled {
409+
tweetMsg := fmt.Sprintf(
410+
"🦆 New Block!\n\n"+
411+
"Pool: %s\n"+
412+
"Tx: %d | Size: %.2fKB\n"+
413+
"Epoch: %d | Lifetime: %d\n\n"+
414+
"pooltool.io/realtime/%d",
415+
i.poolName, blockEvent.Payload.TransactionCount, blockSizeKB,
416+
i.epochBlocks, i.totalBlocks,
417+
blockEvent.Context.BlockNumber)
418+
419+
err = i.sendTweet(tweetMsg, duckImageURL)
420+
if err != nil {
421+
log.Printf("failed to send tweet: %s", err)
422+
}
423+
}
370424
}
371425

372426
// Print the received event information
@@ -432,6 +486,47 @@ func getDuckImage() string {
432486
return imageURL
433487
}
434488

489+
// Download image from URL to bytes
490+
func downloadImage(imageURL string) ([]byte, error) {
491+
resp, err := http.Get(imageURL)
492+
if err != nil {
493+
return nil, fmt.Errorf("failed to download image: %w", err)
494+
}
495+
defer resp.Body.Close()
496+
497+
if resp.StatusCode != http.StatusOK {
498+
return nil, fmt.Errorf("failed to download image: status %d", resp.StatusCode)
499+
}
500+
501+
imageData, err := io.ReadAll(resp.Body)
502+
if err != nil {
503+
return nil, fmt.Errorf("failed to read image data: %w", err)
504+
}
505+
506+
return imageData, nil
507+
}
508+
509+
// Send tweet with optional image
510+
func (i *Indexer) sendTweet(text string, imageURL string) error {
511+
if !i.twitterEnabled || i.twitterClient == nil {
512+
return nil
513+
}
514+
515+
// Create tweet request
516+
input := &types.CreateInput{
517+
Text: gotwi.String(text),
518+
}
519+
520+
// Post the tweet
521+
_, err := managetweet.Create(context.Background(), i.twitterClient, input)
522+
if err != nil {
523+
return fmt.Errorf("failed to post tweet: %w", err)
524+
}
525+
526+
log.Println("Tweet posted successfully")
527+
return nil
528+
}
529+
435530
// Convert to bech32 poolID
436531
func convertToBech32(hash string) (string, error) {
437532
bytes, err := hex.DecodeString(hash)

0 commit comments

Comments
 (0)