Add commands to like and unlike tweets

This commit is contained in:
Alessio 2023-06-25 22:18:00 -03:00
parent d85259a014
commit cc632e0654
5 changed files with 133 additions and 0 deletions

View File

@ -348,6 +348,11 @@ tw --session Offline_Twatter search "from:michaelmalice constitution"
test $(sqlite3 twitter.db "select count(*) from tweets where user_id = 44067298 and text like '%constitution%'") -gt "30" # Not sure exactly how many
# Test liking and unliking
tw --session Offline_Twatter like_tweet https://twitter.com/elonmusk/status/1589023388676554753
tw --session Offline_Twatter unlike_tweet https://twitter.com/elonmusk/status/1589023388676554753
# TODO: Maybe this file should be broken up into multiple test scripts
echo -e "\033[32mAll tests passed. Finished successfully.\033[0m"

View File

@ -60,7 +60,12 @@ This application downloads tweets from twitter and saves them in a SQLite databa
search
<TARGET> is the search query. Should be wrapped in quotes if it has spaces.
(Requires authentication)
like_tweet
unlike_tweet
"Like" or un-"like" the tweet indicated by <TARGET>.
(Requires authentication)
<flags>:
-h, --help
Print this message, then exit.

View File

@ -144,6 +144,10 @@ func main() {
follow_user(target, false)
case "list_followed":
list_followed()
case "like_tweet":
like_tweet(target)
case "unlike_tweet":
unlike_tweet(target)
default:
die(fmt.Sprintf("Invalid operation: %s", operation), true, 3)
}
@ -318,6 +322,30 @@ func follow_user(handle string, is_followed bool) {
}
}
func unlike_tweet(tweet_identifier string) {
tweet_id, err := extract_id_from(tweet_identifier)
if err != nil {
die(err.Error(), false, -1)
}
err = scraper.UnlikeTweet(tweet_id)
if err != nil {
die(err.Error(), false, -10)
}
happy_exit("Unliked the tweet.")
}
func like_tweet(tweet_identifier string) {
tweet_id, err := extract_id_from(tweet_identifier)
if err != nil {
die(err.Error(), false, -1)
}
err = scraper.LikeTweet(tweet_id)
if err != nil {
die(err.Error(), false, -10)
}
happy_exit("Liked the tweet.")
}
func list_followed() {
for _, handle := range profile.GetAllFollowedUsers() {
fmt.Println(handle)

View File

@ -218,6 +218,12 @@ func (api *API) do_http_POST(url string, body string, result interface{}) error
api.add_authentication_headers(req)
log.Debug(fmt.Sprintf("POST: %s\n", req.URL.String()))
for header := range req.Header {
log.Debug(fmt.Sprintf(" %s: %s\n", header, req.Header.Get(header)))
}
log.Debug(" " + body)
resp, err := api.Client.Do(req)
if err != nil {
return fmt.Errorf("Error executing HTTP POST request:\n %w", err)

View File

@ -0,0 +1,89 @@
package scraper
import (
"errors"
"fmt"
"strings"
log "github.com/sirupsen/logrus"
)
var AlreadyLikedThisTweet error = errors.New("already liked this tweet")
var HaventLikedThisTweet error = errors.New("Haven't liked this tweet")
func (api API) LikeTweet(id TweetID) error {
type LikeResponse struct {
Data struct {
FavoriteTweet string `json:"favorite_tweet"`
} `json:"data"`
Errors []struct {
Message string `json:"message"`
Code int `json:"code"`
Kind string `json:"kind"`
Name string `json:"name"`
} `json:"errors"`
}
var result LikeResponse
err := api.do_http_POST(
"https://twitter.com/i/api/graphql/lI07N6Otwv1PhnEgXILM7A/FavoriteTweet",
"{\"variables\":{\"tweet_id\":\""+fmt.Sprint(id)+"\"},\"queryId\":\"lI07N6Otwv1PhnEgXILM7A\"}",
&result,
)
if err != nil {
return fmt.Errorf("Error executing the HTTP POST request:\n %w", err)
}
if len(result.Errors) > 0 {
if strings.Contains(result.Errors[0].Message, "has already favorited tweet") {
return AlreadyLikedThisTweet
}
}
if result.Data.FavoriteTweet != "Done" {
panic(fmt.Sprintf("Dunno why but it failed with value %q", result.Data.FavoriteTweet))
}
return nil
}
func (api API) UnlikeTweet(id TweetID) error {
type UnlikeResponse struct {
Data struct {
UnfavoriteTweet string `json:"unfavorite_tweet"`
} `json:"data"`
Errors []struct {
Message string `json:"message"`
Code int `json:"code"`
Kind string `json:"kind"`
Name string `json:"name"`
} `json:"errors"`
}
var result UnlikeResponse
err := api.do_http_POST(
"https://twitter.com/i/api/graphql/ZYKSe-w7KEslx3JhSIk5LA/UnfavoriteTweet",
"{\"variables\":{\"tweet_id\":\""+fmt.Sprint(id)+"\"},\"queryId\":\"ZYKSe-w7KEslx3JhSIk5LA\"}",
&result,
)
if err != nil {
return fmt.Errorf("Error executing the HTTP POST request:\n %w", err)
}
if len(result.Errors) > 0 {
if strings.Contains(result.Errors[0].Message, "not found in actor's") {
return HaventLikedThisTweet
}
}
if result.Data.UnfavoriteTweet != "Done" {
panic(fmt.Sprintf("Dunno why but it failed with value %q", result.Data.UnfavoriteTweet))
}
return nil
}
func LikeTweet(id TweetID) error {
if !the_api.IsAuthenticated {
log.Fatalf("Must be authenticated!")
}
return the_api.LikeTweet(id)
}
func UnlikeTweet(id TweetID) error {
if !the_api.IsAuthenticated {
log.Fatalf("Must be authenticated!")
}
return the_api.UnlikeTweet(id)
}