Commit fadd4f42 authored by Ubuntu's avatar Ubuntu

add project keys and token ok

parent 98d77422
package main
import (
"encoding/json"
"testing"
)
func TestMe(t *testing.T) {
//cli := NewOAuth2Client()
cli := NewOAuth2Client0816()
me, err := cli.Me()
if err != nil {
t.Error(err)
}
t.Log("Me")
meAsJson, err := json.Marshal(me)
if err != nil {
t.Error(err)
}
t.Log(string(meAsJson))
}
// func TestCreateTweet(t *testing.T) {
// cli := NewOAuth2Client()
// //cli := NewOAuth2ClientSelf()
// res, err := cli.CreateTweet("Hello world!")
// if err != nil {
// t.Error(err)
// }
// t.Log("Me")
// meAsJson, err := json.Marshal(res)
// if err != nil {
// t.Error(err)
// }
// t.Log(string(meAsJson))
// }
// func TestFollowers(t *testing.T) {
// //cli := NewOAuth2Client()
// //cli = NewOAuth2ClientSelf()
// cli := NewOAuth2Client0816()
// users, rateLinmit, next, err := cli.Followers("1783145144700874752", "")
// if err != nil {
// t.Error(err)
// }
// t.Log("next", next)
// meAsJson, err := json.Marshal(users)
// if err != nil {
// t.Error(err)
// }
// t.Log(string(meAsJson))
// rAsJson, err := json.Marshal(rateLinmit)
// if err != nil {
// t.Error(err)
// }
// t.Log(string(rAsJson))
// }
//1800805503066661056
func TestLike(t *testing.T) {
//cli := NewOAuth2Client()
cli := NewOAuth2Client0817()
users, next, rateLimit, err := cli.TweetLikedUsers("1800805503066661056", "")
if err != nil {
t.Error(err)
}
t.Log("next", next)
for k, v := range users {
t.Logf("k %v v %v \n", k, v)
}
rAsJson, err := json.Marshal(rateLimit)
if err != nil {
t.Error(err)
}
t.Log(string(rAsJson))
}
func TestRetweet(t *testing.T) {
//cli := NewOAuth2Client()
cli := NewOAuth2Client0816()
users, next, rateLimit, err := cli.Retweeters("1800805503066661056", "")
if err != nil {
t.Error(err)
}
t.Log("next", next)
for k, v := range users {
t.Logf("k %v v %v \n", k, v)
}
rAsJson, err := json.Marshal(rateLimit)
if err != nil {
t.Error(err)
}
t.Log(string(rAsJson))
}
// TweetSearchAll
func TestTweetSearchAll(t *testing.T) {
cli := NewOAuth2Client()
//cli := NewOAuth2Client2()
users, err := cli.TweetTweetLookup("1800805503066661056")
if err != nil {
t.Error(err)
}
rAsJson, err := json.Marshal(users)
if err != nil {
t.Error(err)
}
t.Log(string(rAsJson))
}
......@@ -5,10 +5,10 @@ func Controller(done <-chan interface{}, inStream <-chan taskInterface) (<-chan
return nil, nil
}
func Idx(done <-chan interface{}, inStream <-chan taskInterface) (<-chan taskInterface, <-chan taskInterface) {
// func Idx(done <-chan interface{}, inStream <-chan taskInterface) (<-chan taskInterface, <-chan taskInterface) {
return nil, nil
}
// return nil, nil
// }
func Scheduler(done <-chan interface{}, inStream <-chan taskInterface) (<-chan taskInterface, <-chan taskInterface) {
......
......@@ -53,6 +53,8 @@ func getMonthEnd(t time.Time) time.Time {
func init() {
rateLimit = make(map[string]twitter.RateLimit)
rateLimit[ProjectMonthKey] = twitter.RateLimit{
Limit: 10000,
Reset: twitter.Epoch(getMonthEnd(time.Now()).Unix()),
......
......@@ -30,10 +30,10 @@ type Profile struct {
*twitterscraper.Profile
}
type NewTask[T FollowTask | RetweetTask] struct {
Task T
Init bool
}
// type NewTask[T FollowTask | RetweetTask] struct {
// Task T
// Init bool
// }
type FollowTask struct {
// URL string
......
......@@ -24,7 +24,7 @@ func VerifyRetweeterInDb(tweetId, retweeter string) (bool, error) {
func AddTaskInsertOrUpdate(req AddTaskReq) error {
task := Task{
task := TaskInDB{
User: req.User,
TaskType: req.TaskType,
TaskId: req.TaskId,
......@@ -55,3 +55,42 @@ func StopTaskUpdate(req AddTaskReq) error {
return err
}
type ProjectInDb struct {
ProjectReq
UserInfo
Available bool `json:"available"`
}
type UserInfo struct {
UserId string `json:"user_id"`
UserName string `json:"username"`
Name string `json:"name"`
}
func AddOrUpdateProject(cfg ProjectReq, user UserInfo) error {
project := ProjectInDb{
ProjectReq: cfg,
UserInfo: user,
Available: true,
}
res, _, err := client.From("project").Insert(project, true, "", "representation", "").Execute()
_ = res
return err
}
func QueryProject(cfg ProjectReq) (bool, error) {
_, count, err := client.From("project").Select("*", "exact", false).Eq("api_key", cfg.ApiKey).Eq("api_key_secret", cfg.ApiKeySecrect).Eq("token", cfg.Token).Eq("access_token", cfg.AccessToken).Eq("access_token_secret", cfg.AccessTokenSecret).Execute()
if err != nil {
return false, err
}
return count == 1, nil
}
package main
import "golang.org/x/time/rate"
type Task struct {
Idx *PageUsers
cli *Client
}
func NewTask() *Task {
p := NewPageUsers(NewIdx([]UserTask{}))
return &Task{
Idx: p,
}
}
type LikingUserTask struct {
task *Task
taskId string
Ratelimiter *rate.Limiter // 需要和单个或两个的cli rate limit一致;
}
func NewLikingTask() {
}
func (l *LikingUserTask) Request() {
users, err := l.task.Idx.Request("", "", l.task.cli.TweetLikingUsers)
_, _ = users, err
}
type RetweeterTask struct {
task *Task
taskId string
Ratelimiter *rate.Limiter // 需要和单个或两个的cli rate limit一致;
}
func NewRetweeterTask() {
}
func (r *RetweeterTask) Request() {
users, err := r.task.Idx.Request("", "", r.task.cli.TweetLikingUsers)
_, _ = users, err
}
......@@ -32,6 +32,12 @@ type VerifyRes struct {
} `json:"data"`
}
type ProjectRes struct {
Code int64 `json:"code"`
Msg string `json:"msg"`
Data UserInfo `json:"data"`
}
/*
{
......@@ -43,74 +49,94 @@ type VerifyRes struct {
*/
type AddTaskReq struct {
// AddOrStop bool
User string `json:"user"`
TaskType string `json:"task_type"`
TaskId string `json:"task_id"`
type ProjectReq struct {
Config
Project string `json:"project"`
}
var taskIn chan<- taskInterface
func TaskAdd(c *fiber.Ctx) error {
//fmt.Println(string(c.Request().Body()))
func Project(c *fiber.Ctx) error {
slog.Info(c.Route().Path, "body", string(c.Request().Body()))
req := AddTaskReq{}
req := ProjectReq{}
if err := json.Unmarshal(c.Request().Body(), &req); err != nil {
slog.Error("json.Unmarshal(c.Request().Body(), &req)", "err", err.Error())
return c.JSON(Res{
return c.JSON(ProjectRes{
Code: 500,
Msg: err.Error(),
})
}
slog.Info(c.Route().Path, "user", req.User, "TaskType", req.TaskType, "TaskId", req.TaskId)
slog.Info("cfg", "project", req.Project, "ApiKey", req.ApiKey, "req.ApiKeySecrect", req.ApiKeySecrect, "req.AccessToken", req.AccessToken, "req.AccessTokenSecret", req.AccessTokenSecret, "req.Token", req.Token)
if req.TaskType == "" || req.TaskId == "" {
return c.JSON(Res{
ok, err := QueryProject(req)
if err != nil {
slog.Error("QueryProject", "err", err.Error())
return c.JSON(ProjectRes{
Code: 500,
Msg: "must provide TaskId and TaskType",
Msg: err.Error(),
})
}
var task taskInterface
if req.TaskType == FollowType {
task = NewFollowTask(req.TaskId, req.User, req.TaskType)
}
if ok {
return c.JSON(ProjectRes{
Code: 500,
Msg: "already existed",
})
if req.TaskType == RetweetType {
task = NewRetweetTask(req.TaskId, req.User, req.TaskType)
}
taskIn <- task
cli := NewClient(req.Config, nil)
//Todo
// 校验任务 条件是否存在;
me, err := cli.Me()
// req.AddOrStop = true
err := AddTaskInsertOrUpdate(req)
if err != nil {
slog.Error("me", "err", err.Error())
return c.JSON(ProjectRes{
Code: 500,
Msg: err.Error(),
})
}
//res, _, err := client.From("twitter_task").Insert(req, true, "", "representation", "").Execute()
for _, v := range me {
if err != nil {
user := UserInfo{
UserId: v.User.ID,
UserName: v.User.UserName,
Name: v.User.Name,
}
slog.Error("twitter_syncer insert", "err", err.Error())
return c.JSON(Res{
if err := AddOrUpdateProject(req, user); err != nil {
slog.Error("insert db ", "err", err.Error())
return c.JSON(ProjectRes{
Code: 500,
Msg: err.Error(),
})
}
//slog.Info("twitter_syncer insert", "res", string(res))
return c.JSON(Res{
return c.JSON(ProjectRes{
Code: 200,
Data: UserInfo{
UserId: v.User.ID,
UserName: v.User.UserName,
Name: v.User.Name,
},
})
}
return c.JSON(ProjectRes{
Code: 500,
Msg: "can not find out the user info with me API",
})
}
type AddTaskReq struct {
// AddOrStop bool
User string `json:"user"`
TaskType string `json:"task_type"`
TaskId string `json:"task_id"`
}
func TaskStop(c *fiber.Ctx) error {
......@@ -153,30 +179,29 @@ func TaskStop(c *fiber.Ctx) error {
}
func VerifyFollower(c *fiber.Ctx) error {
func VerifyRetweeter(c *fiber.Ctx) error {
userId := c.Query("user_id")
followerId := c.Query("follower_id")
tweetId := c.Query("tweet_id")
retweeterId := c.Query("retweeter_id")
if len(userId) == 0 || len(followerId) == 0 {
slog.Error("VerifyFollower", "userId", userId, "followerId", followerId)
if len(tweetId) == 0 || len(retweeterId) == 0 {
slog.Error("VerifyFollower", "tweetId", tweetId, "retweeterId", retweeterId)
return c.JSON(Res{
Code: 500,
Msg: fmt.Sprintf("must provide userId [%v] and followerId [%v]", userId, followerId),
Msg: fmt.Sprintf("must provide tweetId [%v] and retweeterId [%v]", tweetId, retweeterId),
})
}
followerUserName := c.Query("follower_username")
slog.Info(c.Route().Path, "user_id", userId, "followerId", followerId, "followerUserName", followerUserName)
slog.Info(c.Route().Path, "tweetId", tweetId, "retweeterId", retweeterId)
ok, err := VerifyFollowerInDb(userId, followerId)
ok, err := VerifyRetweeterInDb(tweetId, retweeterId)
if err != nil {
slog.Error("VerifyFollowerInDb", "userId", userId, "followerId", followerId, "err", err.Error())
slog.Error("VerifyRetweeter", "tweetId", tweetId, "retweeterId", retweeterId, "err", err.Error())
return c.JSON(Res{
Code: 500,
Msg: fmt.Sprintf("VerifyFollowerInDb userId %v followerId %v err %v", userId, followerId, err.Error()),
//Msg: fmt.Sprint("VerifyFollowerInDb", "tweetId", tweetId, "retweeterId", retweeterId, "err", err.Error()),
Msg: fmt.Sprintf("VerifyRetweeter tweetId %v retweeterId %v err %v", tweetId, retweeterId, err.Error()),
})
}
......@@ -191,29 +216,30 @@ func VerifyFollower(c *fiber.Ctx) error {
}
func VerifyRetweeter(c *fiber.Ctx) error {
func VerifyFollower(c *fiber.Ctx) error {
tweetId := c.Query("tweet_id")
retweeterId := c.Query("retweeter_id")
userId := c.Query("user_id")
followerId := c.Query("follower_id")
if len(tweetId) == 0 || len(retweeterId) == 0 {
slog.Error("VerifyFollower", "tweetId", tweetId, "retweeterId", retweeterId)
if len(userId) == 0 || len(followerId) == 0 {
slog.Error("VerifyFollower", "userId", userId, "followerId", followerId)
return c.JSON(Res{
Code: 500,
Msg: fmt.Sprintf("must provide tweetId [%v] and retweeterId [%v]", tweetId, retweeterId),
Msg: fmt.Sprintf("must provide userId [%v] and followerId [%v]", userId, followerId),
})
}
slog.Info(c.Route().Path, "tweetId", tweetId, "retweeterId", retweeterId)
followerUserName := c.Query("follower_username")
ok, err := VerifyRetweeterInDb(tweetId, retweeterId)
slog.Info(c.Route().Path, "user_id", userId, "followerId", followerId, "followerUserName", followerUserName)
ok, err := VerifyFollowerInDb(userId, followerId)
if err != nil {
slog.Error("VerifyRetweeter", "tweetId", tweetId, "retweeterId", retweeterId, "err", err.Error())
slog.Error("VerifyFollowerInDb", "userId", userId, "followerId", followerId, "err", err.Error())
return c.JSON(Res{
Code: 500,
//Msg: fmt.Sprint("VerifyFollowerInDb", "tweetId", tweetId, "retweeterId", retweeterId, "err", err.Error()),
Msg: fmt.Sprintf("VerifyRetweeter tweetId %v retweeterId %v err %v", tweetId, retweeterId, err.Error()),
Msg: fmt.Sprintf("VerifyFollowerInDb userId %v followerId %v err %v", userId, followerId, err.Error()),
})
}
......
package main
import (
"context"
"fmt"
"net/http"
"time"
"github.com/dghubble/oauth1"
twitter "github.com/g8rswimmer/go-twitter/v2"
"golang.org/x/time/rate"
)
/*
GET /2/tweets/:id/liking_users
5 requests / 15 mins
PER USER
25 requests / 15 mins
PER APP
GET /2/users/:id/liked_tweets
5 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
*/
/*
GET /2/tweets/:id/retweeted_by
5 requests / 15 mins
PER APP
5 requests / 15 mins
PER USER
*/
var LikeRateLimit *rate.Limiter = rate.NewLimiter(rate.Every(3*time.Minute), 1)
var RetweetRateLimit *rate.Limiter = rate.NewLimiter(rate.Every(3*time.Minute), 1)
type authorize struct {
Token string
}
func (a authorize) Add(req *http.Request) {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Token))
}
type Client struct {
*twitter.Client
Ratelimiter *rate.Limiter
}
type Config struct {
ApiKey string `json:"api_key"`
ApiKeySecrect string `json:"api_key_secret"`
AccessToken string `json:"access_token"`
AccessTokenSecret string `json:"access_token_secret"`
Token string `json:"token"`
}
func NewLikeClient(cfg Config) *Client {
return NewClient(cfg, LikeRateLimit)
}
func NewRetweeterClient(cfg Config) *Client {
return NewClient(cfg, RetweetRateLimit)
}
func NewClient(cfg Config, rts *rate.Limiter) *Client {
//twitterAPIKey := "lVnj6Ox9HPcI4LwArSSYU7Pba" //os.Getenv("TWITTER_API_KEY")
//twitterAPIKeySecret := "QMSnWG4QwyXWBVW2hQazzxhw9cSjd32CDfXGkg2DEaUUdscCRZ" //os.Getenv("TWITTER_API_KEY_SECRET")
oauth1Config := oauth1.NewConfig(cfg.ApiKey, cfg.ApiKeySecrect)
//twitterAccessToken := "1783145144700874752-TqHrsFUL20fEz4nz71yYlYVihkGmZn" //os.Getenv("TWITTER_ACCESS_TOKEN")
//twitterAccessTokenSecret := "QDmtDfsiMigTJk1iqoyq8zCHNQJq5zCeC560NH9T5yUZl" //os.Getenv("TWITTER_ACCESS_TOKEN_SECRET")
twitterHttpClient := oauth1Config.Client(oauth1.NoContext, &oauth1.Token{
Token: cfg.AccessToken,
TokenSecret: cfg.AccessTokenSecret,
})
twitterClient := &twitter.Client{
Authorizer: authorize{
Token: cfg.Token,
//Token: "AAAAAAAAAAAAAAAAAAAAAEaPvQEAAAAAWDyrWaIbZHPYeg3ifnvXWdlylvs%3D7XFIO4y2HA0suNxLv570AsaIfmWD4x6XB64zHd9saEqVAhuTMq",
},
Client: twitterHttpClient, //http.DefaultClient,
Host: "https://api.twitter.com",
}
return &Client{
Client: twitterClient,
Ratelimiter: rts,
}
}
func NewOAuth2Client0817() *Client {
twitterAPIKey := "lVnj6Ox9HPcI4LwArSSYU7Pba" //os.Getenv("TWITTER_API_KEY")
twitterAPIKeySecret := "QMSnWG4QwyXWBVW2hQazzxhw9cSjd32CDfXGkg2DEaUUdscCRZ" //os.Getenv("TWITTER_API_KEY_SECRET")
oauth1Config := oauth1.NewConfig(twitterAPIKey, twitterAPIKeySecret)
twitterAccessToken := "1783145144700874752-TqHrsFUL20fEz4nz71yYlYVihkGmZn" //os.Getenv("TWITTER_ACCESS_TOKEN")
twitterAccessTokenSecret := "QDmtDfsiMigTJk1iqoyq8zCHNQJq5zCeC560NH9T5yUZl" //os.Getenv("TWITTER_ACCESS_TOKEN_SECRET")
twitterHttpClient := oauth1Config.Client(oauth1.NoContext, &oauth1.Token{
Token: twitterAccessToken,
TokenSecret: twitterAccessTokenSecret,
})
twitterClient := &twitter.Client{
Authorizer: authorize{
Token: "AAAAAAAAAAAAAAAAAAAAAEaPvQEAAAAAWDyrWaIbZHPYeg3ifnvXWdlylvs%3D7XFIO4y2HA0suNxLv570AsaIfmWD4x6XB64zHd9saEqVAhuTMq",
},
Client: twitterHttpClient, //http.DefaultClient,
Host: "https://api.twitter.com",
}
return &Client{
Client: twitterClient,
}
}
func (c *Client) Retweeters(tweetId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
// ctx is generated here only to use with Ratelimiter
// TODO: Fix performance by removing unneeded allocaton here
ctx := context.Background()
err := c.Ratelimiter.Wait(ctx) // This is a blocking call.
if err != nil {
return nil, "", nil, err
}
opts := twitter.UserRetweetLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
PaginationToken: next,
}
userResponse, err := c.Client.UserRetweetLookup(context.Background(), tweetId, opts)
if err != nil {
return nil, "", nil, err
}
return userResponse.Raw.Users, userResponse.Meta.NextToken, userResponse.RateLimit, nil
}
func (c *Client) TweetLikingUsers(tweetId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
// ctx is generated here only to use with Ratelimiter
// TODO: Fix performance by removing unneeded allocaton here
ctx := context.Background()
err := c.Ratelimiter.Wait(ctx) // This is a blocking call.
if err != nil {
return nil, "", nil, err
}
opts := twitter.TweetLikesLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
TweetFields: []twitter.TweetField{twitter.TweetFieldCreatedAt, twitter.TweetFieldConversationID, twitter.TweetFieldAttachments},
PaginationToken: next,
}
fmt.Println("Callout to tweet like lookup callout")
tweetResponse, err := c.Client.TweetLikesLookup(context.Background(), tweetId, opts)
if err != nil {
return nil, "", nil, err
}
return tweetResponse.Raw.Users, tweetResponse.Meta.NextToken, tweetResponse.RateLimit, nil
}
func (c *Client) Me() (map[string]*twitter.UserDictionary, error) {
// ctx is generated here only to use with Ratelimiter
// TODO: Fix performance by removing unneeded allocaton here
if c.Ratelimiter != nil {
ctx := context.Background()
err := c.Ratelimiter.Wait(ctx) // This is a blocking call.
if err != nil {
return nil, err
}
}
opts := twitter.UserLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
}
userResponse, err := c.Client.AuthUserLookup(context.Background(), opts)
if err != nil {
return nil, err
}
dictionaries := userResponse.Raw.UserDictionaries()
return dictionaries, nil
}
/*
先不考虑单个app cli异常;
确认根据数量 交替,还是根据时间;优先时间;
*/
type TwoClient struct {
cli1 *Client
cli2 *Client
//queue chan *Client
count int
}
func NewTwoClient() {
//先确认,周期是怎么计算的,确定能不能交叉;
}
func (c *TwoClient) Retweeters(tweetId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
//for c := range c.queue {
// return c.Retweeters(tweetId, next)
//}
if c.count < 6 {
return c.cli1.Retweeters(tweetId, next)
}
return c.cli1.Retweeters(tweetId, next)
//return nil, "", nil, fmt.Errorf("two client queue has closed")
}
func (c *TwoClient) TweetLikingUsers(tweetId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
return nil, "", nil, fmt.Errorf("two client queue has closed")
}
func (c *TwoClient) Me() (map[string]*twitter.UserDictionary, error) {
// for c := range c.queue {
// return c.Me()
// }
return nil, fmt.Errorf("two client queue has closed")
}
/*
func NewOAuth2ClientSelf() *Client {
twitterAPIKey := "Ufhj9NggOmRb61LTYUinaDHws" //os.Getenv("TWITTER_API_KEY")
twitterAPIKeySecret := "IfsfhxpyKqmYaEkyB89uH5tT8Ma77FJqrB0BsFN7uUnNX0UZ4B" //os.Getenv("TWITTER_API_KEY_SECRET")
oauth1Config := oauth1.NewConfig(twitterAPIKey, twitterAPIKeySecret)
twitterAccessToken := "1823984946710765569-9Nj7JZaBKQQiTnSyFiOBC3ADnepQqR" //os.Getenv("TWITTER_ACCESS_TOKEN")
twitterAccessTokenSecret := "MltXbwW8Rrb6DJKJo3qnG3lHMUWZ6ILCcqFnujfHrZ875" //os.Getenv("TWITTER_ACCESS_TOKEN_SECRET")
twitterHttpClient := oauth1Config.Client(oauth1.NoContext, &oauth1.Token{
Token: twitterAccessToken,
TokenSecret: twitterAccessTokenSecret,
})
twitterClient := &twitter.Client{
Authorizer: authorize{
Token: "AAAAAAAAAAAAAAAAAAAAAD6AvQEAAAAApN304Hsb89%2FMWG2RoLfSEgb2RS0%3DVQin0pVyPOsOBkCPFoy4wQKOXh3nBvoxMqQ6dc7ulaJ2anvoCm",
},
Client: twitterHttpClient,
Host: "https://api.twitter.com",
}
return &Client{
Client: twitterClient,
}
}
func NewOAuth2Client0816() *Client {
twitterAPIKey := "lVnj6Ox9HPcI4LwArSSYU7Pba" //os.Getenv("TWITTER_API_KEY")
twitterAPIKeySecret := "QMSnWG4QwyXWBVW2hQazzxhw9cSjd32CDfXGkg2DEaUUdscCRZ" //os.Getenv("TWITTER_API_KEY_SECRET")
oauth1Config := oauth1.NewConfig(twitterAPIKey, twitterAPIKeySecret)
twitterAccessToken := "1783145144700874752-TqHrsFUL20fEz4nz71yYlYVihkGmZn" //os.Getenv("TWITTER_ACCESS_TOKEN")
twitterAccessTokenSecret := "QDmtDfsiMigTJk1iqoyq8zCHNQJq5zCeC560NH9T5yUZl" //os.Getenv("TWITTER_ACCESS_TOKEN_SECRET")
twitterHttpClient := oauth1Config.Client(oauth1.NoContext, &oauth1.Token{
Token: twitterAccessToken,
TokenSecret: twitterAccessTokenSecret,
})
twitterClient := &twitter.Client{
Authorizer: authorize{
Token: "AAAAAAAAAAAAAAAAAAAAAEaPvQEAAAAAWDyrWaIbZHPYeg3ifnvXWdlylvs%3D7XFIO4y2HA0suNxLv570AsaIfmWD4x6XB64zHd9saEqVAhuTMq",
},
Client: twitterHttpClient,
Host: "https://api.twitter.com",
}
return &Client{
Client: twitterClient,
}
}
*/
package main
import (
"encoding/json"
"testing"
)
func TestMe(t *testing.T) {
//cli := NewOAuth2Client()
cli := NewOAuth2Client0817()
me, err := cli.Me()
if err != nil {
t.Error(err)
}
t.Log("Me")
meAsJson, err := json.Marshal(me)
if err != nil {
t.Error(err)
}
t.Log(string(meAsJson))
}
func TestCfg(t *testing.T) {
cfg := Config{
ApiKey: "lVnj6Ox9HPcI4LwArSSYU7Pba",
ApiKeySecrect: "QMSnWG4QwyXWBVW2hQazzxhw9cSjd32CDfXGkg2DEaUUdscCRZ",
AccessToken: "1783145144700874752-TqHrsFUL20fEz4nz71yYlYVihkGmZn",
AccessTokenSecret: "QDmtDfsiMigTJk1iqoyq8zCHNQJq5zCeC560NH9T5yUZl",
Token: "AAAAAAAAAAAAAAAAAAAAAEaPvQEAAAAAWDyrWaIbZHPYeg3ifnvXWdlylvs%3D7XFIO4y2HA0suNxLv570AsaIfmWD4x6XB64zHd9saEqVAhuTMq",
}
cfgAsJson, err := json.Marshal(cfg)
if err != nil {
t.Fatal(err)
}
t.Log(string(cfgAsJson))
}
package main
import (
"encoding/json"
"log/slog"
_ "code.wuban.net.cn/odysseus/twitter_syncer/docs"
......@@ -33,21 +32,21 @@ func init() {
}
}
func TwitterAccountFromDB() ([]TwitterAccount, error) {
// func TwitterAccountFromDB() ([]TwitterAccount, error) {
data, count, err := client.From("account").Select("*", "exact", false).Eq("available", "true").Execute()
// data, count, err := client.From("account").Select("*", "exact", false).Eq("available", "true").Execute()
if err != nil {
return nil, err
}
// if err != nil {
// return nil, err
// }
slog.Info("TwitterAccountFromDB", "count", count)
// slog.Info("TwitterAccountFromDB", "count", count)
res := make([]TwitterAccount, 0, count)
// res := make([]TwitterAccount, 0, count)
if err := json.Unmarshal(data, &res); err != nil {
return nil, err
}
// if err := json.Unmarshal(data, &res); err != nil {
// return nil, err
// }
return res, nil
}
// return res, nil
// }
package main
import (
"testing"
)
// func TestQueryTask(t *testing.T) {
func TestQueryTask(t *testing.T) {
// tasks, err := QueryAllTask()
tasks, err := QueryAllTask()
if err != nil {
t.Fatal(err)
}
// if err != nil {
// t.Fatal(err)
// }
for k, v := range tasks {
// for k, v := range tasks {
t.Log(k, "v.User", v.User, "v.TaskType", v.TaskType, "v.TaskId", v.TaskId)
}
// t.Log(k, "v.User", v.User, "v.TaskType", v.TaskType, "v.TaskId", v.TaskId)
// }
}
// }
// func TestInsertTask(t *testing.T) {
......@@ -34,16 +30,16 @@ func TestQueryTask(t *testing.T) {
// // fmt.Println("found", ok, task)
// }
func TestTwitterAccountFromDB(t *testing.T) {
// func TestTwitterAccountFromDB(t *testing.T) {
accounts, err := TwitterAccountFromDB()
if err != nil {
t.Fatal(err)
}
// accounts, err := TwitterAccountFromDB()
// if err != nil {
// t.Fatal(err)
// }
for k, v := range accounts {
t.Log(k, v.User, v.PassWd)
}
// for k, v := range accounts {
// t.Log(k, v.User, v.PassWd)
// }
// fmt.Println("found", ok, task)
}
// // fmt.Println("found", ok, task)
// }
......@@ -61,6 +61,34 @@ x-tagGroups:
- store_model
paths:
/project:
post:
tags:
- Task
requestBody:
content:
application/json:
schema:
type: object
$ref: "#/components/schemas/Project"
responses:
"200":
description: successful operation
content:
application/json:
schema:
type: object
properties:
code:
type: integer
format: int64
enum: [200, 500]
msg:
type: string
data:
type: object
$ref: "#/components/schemas/Me"
/task/add:
summary: Create or update a new task in the twitter syncer
post:
......@@ -223,6 +251,38 @@ components:
write:pets: modify pets in your account
type: oauth2
schemas:
Project:
required:
- api_key
- api_key_secret
- access_token
- access_token_secret
- token
- project
type: object
properties:
api_key:
type: string
api_key_secret:
type: string
access_token:
type: string
access_token_secret:
type: string
token:
type: string
project:
type: string
Me:
type: object
properties:
user_id:
type: string
username:
type: string
name:
type: string
AddTaskReq:
required:
- task_id
......
......@@ -9,6 +9,7 @@ require (
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/dghubble/oauth1 v0.7.3 // indirect
github.com/g8rswimmer/go-twitter/v2 v2.1.5 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
......@@ -42,8 +43,10 @@ require (
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.23.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
......
......@@ -17,6 +17,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE=
github.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY=
github.com/g8rswimmer/go-twitter/v2 v2.1.5 h1:Uj9Yuof2UducrP4Xva7irnUJfB9354/VyUXKmc2D5gg=
github.com/g8rswimmer/go-twitter/v2 v2.1.5/go.mod h1:/55xWb313KQs25X7oZrNSEwLQNkYHhPsDwFstc45vhc=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
......@@ -138,6 +140,8 @@ golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
......@@ -174,6 +178,8 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
......
package main
import (
"encoding/json"
"fmt"
"log/slog"
"container/list"
"time"
twitter "github.com/g8rswimmer/go-twitter/v2"
"github.com/supabase-community/postgrest-go"
)
type streamIdx struct {
cli Client
idx []Task
taskId string
toDbQueue chan []UserTask
// timer time.Timer
/*
处理三种情况:
1. idx 为空;
2. idx 正常匹配
3. idx 没有匹配上
x 4. 到最后也没匹配上,或者超过了多少页。 外面处理;
*/
type Idx struct {
idx []UserTask
newIdx []UserTask
//taskId string
List *list.List
}
func NewIdx(i []UserTask) *Idx {
return &Idx{
idx: i,
newIdx: make([]UserTask, 0, 10),
}
}
func (s *streamIdx) Request(taskType, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
func (s *Idx) Idx(page []UserTask) (bool, *list.List) {
if len(s.newIdx) == 0 {
for k, v := range page {
s.newIdx = append(s.newIdx, v)
if k > 5 {
break
}
}
}
if s.idx != nil && len(s.idx) == 0 {
switch taskType {
case FollowType:
return s.cli.Followers(s.taskId, next)
case RetweetType:
return s.cli.Retweeters(s.taskId, next)
case TweetLikingUsersType:
return s.cli.TweetLikingUsers(s.taskId, next)
newList := list.New()
for _, v := range page {
newList.PushFront(v)
}
s.idx = s.newIdx
s.newIdx = make([]UserTask, 0, 10)
return true, newList
}
//TODO 匹配多个元素,防止用户取消;
for k, v := range page {
for ik, iv := range s.idx {
if v.UserId == iv.UserId {
newList := s.List
s.List = list.New()
//idx
s.idx = s.newIdx
s.newIdx = make([]UserTask, 0, 10)
return true, newList
} else {
s.List.PushFront(v)
}
_, _ = k, ik
}
}
return false, nil
}
return nil, "", nil, fmt.Errorf("currently not suport the task type %v", taskType)
type PageUsers struct {
//cli *Client
idx *Idx
}
func (s *streamIdx) Idx() {
func NewPageUsers(idx *Idx) *PageUsers {
return &PageUsers{
//idx: NewIdx(idx),
idx: idx,
// cli: cli,
}
}
func (s *streamIdx) Scheduler(timer *time.Timer) {
type req func(tweetId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error)
/*
TODO 最后一页的标识,没有处理;
*/
func (p *PageUsers) Request(tweetId string, next string, f req) ([]UserTask, error) {
//users, next, rt, err := p.cli.TweetLikingUsers(tweetId, next)
users, newNext, rt, err := f(tweetId, next)
if err != nil {
return nil, err
}
for t := range timer.C {
_ = t
s.Idx()
//s.Scheduler(time.NewTimer(10 * time.Second))
if rt.Remaining == 0 {
time.Sleep(time.Until(rt.Reset.Time().Add(500 * time.Millisecond)))
}
taskUser := userObjectToUserTask(users)
ok, l := p.idx.Idx(taskUser)
if ok {
res := make([]UserTask, 0, l.Len())
for e := l.Front(); e != nil; e = e.Next() {
if user, ok := e.Value.(UserTask); ok {
res = append(res, user)
}
}
return res, nil
}
return p.Request(tweetId, newNext, f)
}
func userObjectToUserTask(pageUsers []*twitter.UserObj) []UserTask {
res := make([]UserTask, 0, len(pageUsers))
for _, v := range pageUsers {
res = append(res, UserTask{
//TaskId string `json:"task_id"`
UserId: v.ID, //string `json:"user_id"`
UserName: v.UserName, //string `json:"user_name"`
})
}
return res
}
// func scheduler(tick *time.Ticker) {
......@@ -83,123 +183,123 @@ func (s *streamIdx) Scheduler(timer *time.Timer) {
// const RetweetType = "retweet"
type FollowerId struct {
Follower
Id int `json:"id"`
CreatedAt string `json:"created_at"`
}
// type FollowerId struct {
// Follower
// Id int `json:"id"`
// CreatedAt string `json:"created_at"`
// }
type Follower struct {
//user_id
UserId string `json:"user_id"`
Follower string `json:"follower_id"`
UserName string `json:"follower_username"`
}
// type Follower struct {
// //user_id
// UserId string `json:"user_id"`
// Follower string `json:"follower_id"`
// UserName string `json:"follower_username"`
// }
type RetweeterId struct {
Retweeter
Id int `json:"id"`
CreatedAt string `json:"created_at"`
}
// type RetweeterId struct {
// Retweeter
// Id int `json:"id"`
// CreatedAt string `json:"created_at"`
// }
type Retweeter struct {
TweetId string `json:"tweet_id"`
RetweeterId string `json:"retweeter_id"`
RetweeterUserName string `json:"retweeter_username"`
}
// type Retweeter struct {
// TweetId string `json:"tweet_id"`
// RetweeterId string `json:"retweeter_id"`
// RetweeterUserName string `json:"retweeter_username"`
// }
func GetTasksIdx() ([]taskInterface, error) {
// func GetTasksIdx() ([]taskInterface, error) {
tasks, err := QueryAllTask()
// tasks, err := QueryAllTask()
if err != nil {
return nil, err
}
// if err != nil {
// return nil, err
// }
res := make([]taskInterface, 0, 10)
// res := make([]taskInterface, 0, 10)
for _, task := range tasks {
// for _, task := range tasks {
if task.TaskType == FollowType {
// if task.TaskType == FollowType {
data, count, err := client.From("followers").Select("", "user_id", false).
Eq("user_id", task.TaskId).
Order("id", &postgrest.OrderOpts{
Ascending: false,
// NullsFirst bool
// ForeignTable string
}).Range(0, 10, "").Execute()
// data, count, err := client.From("followers").Select("", "user_id", false).
// Eq("user_id", task.TaskId).
// Order("id", &postgrest.OrderOpts{
// Ascending: false,
// // NullsFirst bool
// // ForeignTable string
// }).Range(0, 10, "").Execute()
if err != nil {
slog.Error("select * from followers error", err)
return nil, err
}
_ = count
// if err != nil {
// slog.Error("select * from followers error", err)
// return nil, err
// }
// _ = count
slog.Info("idx data", "user id", task.TaskId, "user name", task.User, "idx", data)
// slog.Info("idx data", "user id", task.TaskId, "user name", task.User, "idx", data)
fmt.Println("idx data", string(data))
// fmt.Println("idx data", string(data))
userRes := make([]FollowerId, 0, 10)
// userRes := make([]FollowerId, 0, 10)
if err := json.Unmarshal(data, &userRes); err != nil {
return nil, err
}
// if err := json.Unmarshal(data, &userRes); err != nil {
// return nil, err
// }
followTask := NewFollowTask(task.TaskId, task.User, task.TaskType)
// followTask := NewFollowTask(task.TaskId, task.User, task.TaskType)
followTask.Idx = userRes
// followTask.Idx = userRes
// if len(userRes) == 0 {
// followTask.Init = true
// }
// // if len(userRes) == 0 {
// // followTask.Init = true
// // }
res = append(res, followTask)
}
// res = append(res, followTask)
// }
if task.TaskType == RetweetType {
// if task.TaskType == RetweetType {
data, count, err := client.From("retweeters").Select("", "tweet_id", false).
Eq("tweet_id", task.TaskId).
Order("id", &postgrest.OrderOpts{
Ascending: false,
// NullsFirst bool
// ForeignTable string
}).Range(0, 10, "").Execute()
// data, count, err := client.From("retweeters").Select("", "tweet_id", false).
// Eq("tweet_id", task.TaskId).
// Order("id", &postgrest.OrderOpts{
// Ascending: false,
// // NullsFirst bool
// // ForeignTable string
// }).Range(0, 10, "").Execute()
if err != nil {
slog.Error("select * from retweeters error", err)
return nil, err
}
_ = count
// if err != nil {
// slog.Error("select * from retweeters error", err)
// return nil, err
// }
// _ = count
slog.Info("idx data", "tweet id", task.TaskId, "user name", task.User, "idx", data)
// slog.Info("idx data", "tweet id", task.TaskId, "user name", task.User, "idx", data)
fmt.Println("idx data", string(data))
// fmt.Println("idx data", string(data))
userRes := make([]RetweeterId, 0, 10)
// userRes := make([]RetweeterId, 0, 10)
if err := json.Unmarshal(data, &userRes); err != nil {
return nil, err
}
// if err := json.Unmarshal(data, &userRes); err != nil {
// return nil, err
// }
retweetTask := NewRetweetTask(task.TaskId, task.User, task.TaskType)
// retweetTask := NewRetweetTask(task.TaskId, task.User, task.TaskType)
//followTask := NewFollowTask(task.TaskId, task.User, task.TaskType)
// //followTask := NewFollowTask(task.TaskId, task.User, task.TaskType)
retweetTask.Idx = userRes
// retweetTask.Idx = userRes
// if len(userRes) == 0 {
// followTask.Init = true
// }
// // if len(userRes) == 0 {
// // followTask.Init = true
// // }
res = append(res, retweetTask)
// res = append(res, retweetTask)
}
}
// }
// }
return res, nil
}
// return res, nil
// }
// func FollowersToBackList(done <-chan interface{}, idxss [][]FollowerId) (<-chan *list.List, error) {
......
package main
import (
"log/slog"
"time"
// "github.com/gofiber/fiber/v2"
// "github.com/gofiber/fiber/v2/middleware/cors"
// "github.com/gofiber/swagger"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/swagger"
// docs are generated by Swag CLI, you have to import them.
// replace with your own docs folder, usually "github.com/username/reponame/docs"
//_ "github.com/gofiber/swagger/example/docs"
)
func mainBak() {
app := fiber.New()
app.Use(cors.New())
// app.Get("/", func(c *fiber.Ctx) error {
// return c.SendString("OK")
// })
app.Static("/*", "./public", fiber.Static{
Compress: true,
ByteRange: true,
Browse: true,
CacheDuration: 10 * time.Second,
MaxAge: 3600,
Download: true,
})
// app.Static("/*", "./public/")
// app.Static("/swagger/docs", "./docs")
// app.Static("/swagger/docs", "./docs")
app.Get("/swagger/*", swagger.New(swagger.Config{ // custom
URL: "http://43.198.54.207:8001/swagger/docs/swagger.yaml", //http://124.193.167.71:8000/
DeepLinking: false,
// Expand ("list") or Collapse ("none") tag groups by default
DocExpansion: "none",
// Prefill OAuth ClientId on Authorize popup
// OAuth: &swagger.OAuthConfig{
// AppName: "OAuth Provider",
// ClientId: "21bb4edc-05a7-4afc-86f1-2e151e4ba6e2",
// },
// Ability to change OAuth2 redirect uri location
//OAuth2RedirectUrl: "http://localhost:8080/swagger/oauth2-redirect.html",
}))
// app.Static("/swagger/docs", "./docs")
// app.Get("/swagger/*", swagger.New(swagger.Config{ // custom
// URL: "http://43.198.54.207:8001/swagger/docs/swagger.yaml", //http://124.193.167.71:8000/
// DeepLinking: false,
// // Expand ("list") or Collapse ("none") tag groups by default
// DocExpansion: "none",
// // Prefill OAuth ClientId on Authorize popup
// // OAuth: &swagger.OAuthConfig{
// // AppName: "OAuth Provider",
// // ClientId: "21bb4edc-05a7-4afc-86f1-2e151e4ba6e2",
// // },
// // Ability to change OAuth2 redirect uri location
// //OAuth2RedirectUrl: "http://localhost:8080/swagger/oauth2-redirect.html",
// }))
if err := app.Listen(":8001"); err != nil {
slog.Error(err.Error())
}
}
......@@ -26,13 +26,13 @@ func main() {
defer close(done)
//go func() {
taskInStream, err := newSync(done)
if err != nil {
panic(err)
}
//}()
// taskInStream, err := newSync(done)
// if err != nil {
// panic(err)
// }
// //}()
taskIn = taskInStream
// taskIn = taskInStream
app := fiber.New()
app.Use(cors.New())
......@@ -53,7 +53,8 @@ func main() {
//OAuth2RedirectUrl: "http://localhost:8080/swagger/oauth2-redirect.html",
}))
app.Post("/task/add", TaskAdd)
app.Post("/project", Project)
//app.Post("/task/add", TaskAdd)
app.Post("/task/stop", TaskStop)
app.Get("/verify/follower", VerifyFollower)
app.Get("/verify/retweeter", VerifyRetweeter)
......
package main
import (
"context"
"fmt"
"net/http"
twitter "github.com/g8rswimmer/go-twitter/v2"
)
//https://github.com/michimani/gotwi.git
const (
OAuthTokenEnvKeyName = "GOTWI_ACCESS_TOKEN"
OAuthTokenSecretEnvKeyName = "GOTWI_ACCESS_TOKEN_SECRET"
)
type authorize struct {
Token string
}
func (a authorize) Add(req *http.Request) {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Token))
}
type Client struct {
*twitter.Client
}
func NewOAuth2Client() *Client {
client := &twitter.Client{
Authorizer: authorize{
Token: "",
},
Client: http.DefaultClient,
Host: "https://api.twitter.com",
}
return &Client{
Client: client,
}
}
func (c *Client) Followers(userId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
opts := twitter.UserFollowersLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
TweetFields: []twitter.TweetField{twitter.TweetFieldContextAnnotations},
PaginationToken: next,
}
fmt.Println("Callout to user followers lookup callout")
userResponse, err := c.Client.UserFollowersLookup(context.Background(), userId, opts)
if err != nil {
return nil, "", nil, err
}
return userResponse.Raw.Users, userResponse.Meta.NextToken, userResponse.RateLimit, nil
}
func (c *Client) Retweeters(tweetId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
opts := twitter.UserRetweetLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
PaginationToken: next,
}
userResponse, err := c.Client.UserRetweetLookup(context.Background(), tweetId, opts)
if err != nil {
return nil, "", nil, err
}
return userResponse.Raw.Users, userResponse.Meta.NextToken, userResponse.RateLimit, nil
}
func (c *Client) TweetLikingUsers(tweetId string, next string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {
opts := twitter.TweetLikesLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
TweetFields: []twitter.TweetField{twitter.TweetFieldCreatedAt, twitter.TweetFieldConversationID, twitter.TweetFieldAttachments},
PaginationToken: next,
}
fmt.Println("Callout to tweet like lookup callout")
tweetResponse, err := c.Client.TweetLikesLookup(context.Background(), tweetId, opts)
if err != nil {
return nil, "", nil, err
}
return tweetResponse.Raw.Users, tweetResponse.Meta.NextToken, tweetResponse.RateLimit, nil
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment