commit 66e906f139ee93ddf6a81ca5824e2ffddc737a84 Author: qugalet Date: Tue Sep 19 22:19:25 2023 +0300 Rewrite in Go Signed-off-by: qugalet diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8ba473e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +domains.txt +nicknames.txt +uabot diff --git a/.env.example b/.env.example new file mode 100755 index 0000000..499e7e8 --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +POSTGRES_HOST= +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_DB= +POSTGRES_PORT= + +MATRIX_HOMESERVER= +MATRIX_USERNAME= +MATRIX_ACCESS_TOKEN= +MATRIX_ROOM_ID= + +MASTODON_HOMESERVER= +MASTODON_CLIENT_ID= +MASTODON_CLIENT_SECRET= +MASTODON_ACCESS_TOKEN= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..469158f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +domains.txt +nicknames.txt +uabot diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a7ec6ca --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.20 + +WORKDIR /app + +COPY go.mod go.sum . + +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux go build -o /app/uabot + +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..4369a76 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +## UA-Bot + +**UA-Bot** - Bot for Mastodon that reboosts message if mentioned. + +### Features + +- Moderation - Send to Matrix room before reblog +- Reply or broken thread detection (not fully tested) +- Block instances and nicknames by regex + +### Install + +First, install Postgres (>= 14 will be fine, i guess). + +Second, clone repo. + +Copy `.env.example` to `.env` and edit with your data + +Also create `nicknames.txt` and `domains.txt` (regex files): + +- `nicknames.txt` - block by username or display name +- `domains.txt` - block by domain name + +Tip: `#` in the beginning of the regex file means that this line will be ignored. You can use it as comment + +Now you can install using **Dockerimage** or **manually**: + +#### Docker + +Build `Dockerfile` and run +Also, mount `domains.txt` and `nicknames.txt` to the `/app` + +### Manually + +Install Go >= 1.20 + +Next, build the project: + +```sh +go build +``` + +Run: + +```sh +./uabot +``` diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..ab53ff9 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +test -f domains.txt || touch domains.txt +test -f nicknames.txt || touch nicknames.txt + +./uabot diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..1d284ff --- /dev/null +++ b/go.mod @@ -0,0 +1,39 @@ +module uabot + +go 1.20 + +require ( + github.com/mattn/go-mastodon v0.0.6 + gorm.io/gorm v1.25.4 +) + +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.3.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/rs/zerolog v1.30.0 // indirect + github.com/tidwall/gjson v1.16.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/sjson v1.2.5 // indirect + go.mau.fi/util v0.1.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect + maunium.net/go/maulogger/v2 v2.4.1 // indirect +) + +require ( + github.com/gorilla/websocket v1.5.0 // indirect + github.com/joho/godotenv v1.5.1 + github.com/sirupsen/logrus v1.9.3 + github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect + gorm.io/driver/postgres v1.5.2 + maunium.net/go/mautrix v0.16.1 +) diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..2a01d26 --- /dev/null +++ b/go.sum @@ -0,0 +1,74 @@ +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU= +github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-mastodon v0.0.6 h1:lqU1sOeeIapaDsDUL6udDZIzMb2Wqapo347VZlaOzf0= +github.com/mattn/go-mastodon v0.0.6/go.mod h1:cg7RFk2pcUfHZw/IvKe1FUzmlq5KnLFqs7eV2PHplV8= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= +github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= +go.mau.fi/util v0.1.0 h1:BwIFWIOEeO7lsiI2eWKFkWTfc5yQmoe+0FYyOFVyaoE= +go.mau.fi/util v0.1.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0= +gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8= +gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= +gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= +maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= +maunium.net/go/mautrix v0.16.1 h1:Wb3CvOCe8A/NLsFeZYxKrgXKiqeZUQEBD1zqm7n/kWk= +maunium.net/go/mautrix v0.16.1/go.mod h1:2Jf15tulVtr6LxoiRL4smRXwpkGWUNfBFhwh/aXDBuk= diff --git a/main.go b/main.go new file mode 100755 index 0000000..60f01be --- /dev/null +++ b/main.go @@ -0,0 +1,296 @@ +package main + +import ( + "bufio" + "context" + "fmt" + "os" + "path" + "regexp" + "strings" + + "github.com/joho/godotenv" + log "github.com/sirupsen/logrus" + + "github.com/mattn/go-mastodon" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "maunium.net/go/mautrix" + m_event "maunium.net/go/mautrix/event" + m_id "maunium.net/go/mautrix/id" +) + +type Post struct { + ID int + PostId string + MessageId string +} + +func parseRegexFile(path string) ([]regexp.Regexp, error) { + file, err := os.Open(path) + + if err != nil { + return nil, err + } + + file_scanner := bufio.NewScanner(file) + + file_scanner.Split(bufio.ScanLines) + + var result []regexp.Regexp + + for file_scanner.Scan() { + text := file_scanner.Text() + if strings.Trim(text, " ")[0] != '#' { + regex, err := regexp.Compile(text) + if err != nil { + return nil, err + } + result = append(result, *regex) + } + } + return result, nil +} + +func main() { + ctx := context.Background() + err := godotenv.Load() + + if err != nil { + log.Fatal(err) + } + + ex, err := os.Getwd() + + if err != nil { + log.Fatal(err) + } + + domainRegexes, err := parseRegexFile(path.Join(ex, "domains.txt")) + + if err != nil { + log.Fatal(err) + } + + nicknameRegexes, err := parseRegexFile(path.Join(ex, "nicknames.txt")) + + if err != nil { + log.Fatal(err) + } + + // log.Info(nicknameRegexes, domainRegexes) + + db, err := gorm.Open(postgres.Open(fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s", + os.Getenv("POSTGRES_HOST"), + os.Getenv("POSTGRES_USER"), + os.Getenv("POSTGRES_PASSWORD"), + os.Getenv("POSTGRES_DB"), + os.Getenv("POSTGRES_PORT"))), &gorm.Config{}) + + db.AutoMigrate(&Post{}) + + if err != nil { + log.Fatal(err) + } + + masto_client := mastodon.NewClient(&mastodon.Config{ + Server: os.Getenv("MASTODON_HOMESERVER"), + ClientID: os.Getenv("MASTODON_CLIENT_ID"), + ClientSecret: os.Getenv("MASTODON_CLIENT_SECRET"), + AccessToken: os.Getenv("MASTODON_ACCESS_TOKEN"), + }) + masto_user, err := masto_client.GetAccountCurrentUser(ctx) + + if err != nil { + log.Fatal(err) + } + + // log.Println(my_account) + + matrix_client, err := mautrix.NewClient(os.Getenv("MATRIX_HOMESERVER"), m_id.UserID(os.Getenv("MATRIX_USERNAME")), os.Getenv("MATRIX_ACCESS_TOKEN")) + matrix_client.Store = mautrix.NewAccountDataStore("ua.in.fediland.uabot", matrix_client) + syncer := matrix_client.Syncer.(*mautrix.DefaultSyncer) + + if err != nil { + log.Fatal(err) + } + log.Info("starting...") + + syncer.OnEventType(m_event.EventReaction, func(source mautrix.EventSource, ev *m_event.Event) { + log.Info("reactions") + // if ev.RoomID == os.Getenv("MATRIX_ROOM_ID") && ev.Content != nil && + content := ev.Content.AsReaction() + key := []rune(content.RelatesTo.Key) + log.Info(content.RelatesTo.Key) + if ev.RoomID.String() == os.Getenv("MATRIX_ROOM_ID") && (key[0] == '👍' || key[0] == '👎') { + log.Info("reacted well") + var post Post + if err := db.First(&post, "message_id = ?", content.RelatesTo.EventID.String()).Error; err != nil { + log.Error(err) + return + } + status, err := masto_client.GetStatus(ctx, mastodon.ID(post.PostId)) + + if err != nil { + log.Error(err) + return + } + + if key[0] == '👍' { + _, err = masto_client.Reblog(ctx, mastodon.ID(post.PostId)) + if err != nil { + log.Error(err) + return + } + // matrix_client.SendMessageEvent(os.Getenv("MATRIX_ROOM_ID"), "m.room.message", interface{}) + body := fmt.Sprintf(`#%d: Схвалено +%s +`, post.ID, status.URL) + + _, err := matrix_client.SendMessageEvent(m_id.RoomID(os.Getenv("MATRIX_ROOM_ID")), m_event.EventMessage, &m_event.MessageEventContent{ + Body: body, + MsgType: m_event.MsgText, + NewContent: &m_event.MessageEventContent{ + MsgType: m_event.MsgText, + Body: body}, + RelatesTo: &m_event.RelatesTo{ + Type: m_event.RelReplace, + EventID: m_id.EventID(post.MessageId), + }, + }) + + if err != nil { + log.Error(err) + } + + } else { + body := fmt.Sprintf(`#%d: Відмовлено +%s +`, post.ID, status.URL) + _, err := matrix_client.SendMessageEvent(m_id.RoomID(os.Getenv("MATRIX_ROOM_ID")), m_event.EventMessage, &m_event.MessageEventContent{ + Body: body, + MsgType: m_event.MsgText, + NewContent: &m_event.MessageEventContent{ + MsgType: m_event.MsgText, + Body: body, + }, + RelatesTo: &m_event.RelatesTo{ + Type: m_event.RelReplace, + EventID: m_id.EventID(post.MessageId), + }, + }) + if err != nil { + log.Error(err) + } + } + } + }) + + events, err := masto_client.StreamingUser(ctx) + + if err != nil { + log.Fatal(err) + } + + go func() { + for { + if err := matrix_client.Sync(); err != nil { + log.Error(fmt.Errorf("Sync() returned %s", err)) + } + } + }() + +notif_loop: + for { + notif_event, ok := (<-events).(*mastodon.NotificationEvent) + if !ok { + continue + } + notif := notif_event.Notification + + // log.Println(notif) + + if notif.Type == "follow" { + acct_parsed := strings.Split(notif.Account.Acct, "@") + + for _, regex := range domainRegexes { + if len(acct_parsed) == 2 && regex.MatchString(acct_parsed[1]) { + _, err := masto_client.AccountBlock(ctx, notif.Account.ID) + if err == nil { + matrix_client.SendText(m_id.RoomID(os.Getenv("MATRIX_ROOM_ID")), fmt.Sprintf("%s заблокований автоматично регексом доменів: %s", notif.Account.Acct, regex.String())) + } + continue notif_loop + } + } + + for _, regex := range nicknameRegexes { + if regex.MatchString(acct_parsed[0]) { + _, err := masto_client.AccountBlock(ctx, notif.Account.ID) + if err == nil { + matrix_client.SendText(m_id.RoomID(os.Getenv("MATRIX_ROOM_ID")), fmt.Sprintf("%s заблокований автоматично регексом нікнеймів: %s", notif.Account.Acct, regex.String())) + } + continue notif_loop + } + } + + masto_client.PostStatus(ctx, &mastodon.Toot{ + Status: fmt.Sprintf("Вітаємо у нашій спільноті! @%s", notif.Account.Acct), + Visibility: mastodon.VisibilityUnlisted, + }) + } + + if notif.Type == "mention" && notif.Status.InReplyToID == nil && notif.Status.InReplyToAccountID == nil { + subscribers, err := masto_client.GetAccountFollowers(ctx, masto_user.ID, &mastodon.Pagination{Limit: 10000}) + if err != nil { + log.Error(err) + continue + } + + ok := false + + for _, subscriber := range subscribers { + if subscriber.ID == notif.Account.ID { + ok = true + break + } + } + + if ok && notif.Status.Visibility == "public" { + if err := db.Create(&Post{ + PostId: string(notif.Status.ID), + }).Error; err != nil { + log.Error(err) + continue + } + var post Post + if err := db.First(&post, "post_id = ?", string(notif.Status.ID)).Error; err != nil { + log.Error(err) + continue + } + message, err := matrix_client.SendText(m_id.RoomID(os.Getenv("MATRIX_ROOM_ID")), fmt.Sprintf(`#%d: На модерації +%s +👍 - Так +👎 - Ні +`, post.ID, notif.Status.URL)) + if err != nil { + log.Fatal(err) + db.Delete(&post, post.ID) + } + + post.MessageId = message.EventID.String() + db.Save(&post) + } + + if ok && notif.Status.Visibility == "direct" && notif.Status.Content == "ping" { + masto_client.PostStatus(ctx, &mastodon.Toot{ + Status: "meow", + InReplyToID: notif.Status.ID, + Visibility: "direct", + }) + } + + } + + } + +}