misc

Misc scripts and tools
git clone git://git.alexwennerberg.com/misc
Log | Files | Refs | README | LICENSE

commit 5fa64600ea38e8c9232472d9851bce53fc3c045c
parent fb3e75a9309854f15d02345293706083a22cc842
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sun, 17 Oct 2021 00:31:21 -0700

Add mastodon delete script

Diffstat:
Amastodon-delete.go | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 125 insertions(+), 0 deletions(-)

diff --git a/mastodon-delete.go b/mastodon-delete.go @@ -0,0 +1,125 @@ +package main + +// Make your mastodon posts ephemeral by deleting old posts +// Dies on rate limit. Designed to periodically run on cron, Not a one-time clear. +// Write a wrapper script if you want that. +// Not thoroughly tested, caveat emptor + +import ( + "bufio" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "strings" + "time" +) + +type MastodonClient struct { + server string + access_token string + oauthClient http.Client +} + +func (client *MastodonClient) do(method string, endpoint string) ([]byte, error) { + request, err := http.NewRequest(method, client.server+endpoint, nil) + request.Header.Add("Authorization", "Bearer "+client.access_token) + if err != nil { + return nil, err + } + resp, err := client.oauthClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("Response code %d: %s", resp.StatusCode, string(body)) + } + return body, nil +} + +func main() { + var configPath = flag.String("c", "config.txt", "config file path") + var days = flag.Int("d", 90, "will delete statuses older than this many days") + flag.Parse() + fmt.Println("Deleting all statuses older than", *days, "days") + file, err := os.Open(*configPath) + if err != nil { + log.Fatal(err) + } + scanner := bufio.NewScanner(file) + client := MastodonClient{} + for scanner.Scan() { + t := scanner.Text() + i := strings.Index(t, "=") + if i == -1 { + log.Fatal("Could not parse config file, line: ", t) + } + v := t[i+1:] + switch k := t[:i]; k { + case "server": + client.server = v + case "access_token": + client.access_token = v + } + } + + resp, err := client.do("GET", "/api/v1/accounts/verify_credentials") + if err != nil { + log.Fatal(err) + } + account := &struct { + Id string `json:"id"` + }{} + err = json.Unmarshal(resp, account) + if err != nil { + log.Println(string(resp)) + log.Fatal(err) + } + + me_id := account.Id + + var max_id string + cutoff := time.Now().UTC().AddDate(0, 0, *days*-1).Format(time.RFC3339) + for { + url := "/api/v1/accounts/" + me_id + "/statuses" + if max_id != "" { + url += "?max_id=" + max_id + } + resp, err = client.do("GET", url) + if err != nil { + log.Fatal(err) + } + statuses := []struct { + Id string + Content string + CreatedAt string `json:"created_at"` + }{} + // Assumes result is already sorted + err = json.Unmarshal(resp, &statuses) + if err != nil { + log.Println(string(resp)) + log.Fatal(err) + } + if len(statuses) == 0 { + break + } + max_id = statuses[len(statuses)-1].Id + for _, status := range statuses { + if status.CreatedAt < cutoff { // RFC 3339 date strings are sortable + fmt.Printf("Deleting status %s: '%s'\n", status.Id, status.Content) + _, err := client.do("DELETE", "/api/v1/statuses/"+status.Id) + if err != nil { + log.Fatal(err) + } + } + } + } +}