UA-Bot/main.go

324 lines
7.8 KiB
Go
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"bufio"
"context"
"fmt"
"os"
"path"
"regexp"
"strings"
"time"
"github.com/joho/godotenv"
log "github.com/sirupsen/logrus"
"github.com/UA-Fediland/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()
ROOM_ID := os.Getenv("MATRIX_ROOM_ID")
log.SetLevel(log.DebugLevel)
if err != nil {
log.Fatal(err)
}
ex, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
blacklistRegexes, err := parseRegexFile(path.Join(ex, "blacklist.txt"))
if err != nil {
log.Fatal(err)
}
whitelistRegexes, err := parseRegexFile(path.Join(ex, "whitelist.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)
}
var subscribers []*mastodon.Account
var pg mastodon.Pagination
log.Debug("get followers list")
for {
fs, err := masto_client.GetAccountFollowers(context.Background(), masto_user.ID, &pg)
if err != nil {
log.Fatal(err)
}
subscribers = append(subscribers, fs...)
// log.Debug(pg)
// log.Debug(pg.MaxID)
if pg.MaxID == "" {
break
}
time.Sleep(200 * time.Millisecond)
}
log.Debugf("subscribers count: %v", len(subscribers))
// for _, subscriber := range subscribers {
// log.Debug(subscriber.Acct)
// }
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...")
matrix_client.SendText(m_id.RoomID(ROOM_ID), "Бот запущений!")
syncer.OnEventType(m_event.EventReaction, func(source mautrix.EventSource, ev *m_event.Event) {
log.Debug("reactions")
// if ev.RoomID == ROOM_ID && ev.Content != nil &&
content := ev.Content.AsReaction()
key := []rune(content.RelatesTo.Key)
log.Debug(content.RelatesTo.Key)
if ev.RoomID.String() == ROOM_ID && (key[0] == '👍' || key[0] == '👎') {
log.Debug("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(ROOM_ID, "m.room.message", interface{})
body := fmt.Sprintf(`#%d: Схвалено
%s
`, post.ID, status.URL)
_, err := matrix_client.SendMessageEvent(m_id.RoomID(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(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 blacklistRegexes {
if regex.MatchString(notif.Account.Acct) {
_, err := masto_client.AccountBlock(ctx, notif.Account.ID)
if err == nil {
matrix_client.SendText(m_id.RoomID(ROOM_ID), fmt.Sprintf("%s заблокований автоматично регулярним виразом: %s", notif.Account.Acct, regex.String()))
}
continue notif_loop
}
}
subscribers = append(subscribers, &notif.Account)
masto_client.PostStatus(ctx, &mastodon.Toot{
Status: fmt.Sprintf("Вітаємо у нашій спільноті! @%s", notif.Account.Acct),
Visibility: mastodon.VisibilityUnlisted,
})
}
if notif.Type == "mention" {
log.Debugf("post %v mentioned bot", notif.Status.ID)
}
if notif.Type == "mention" && notif.Status.InReplyToID == nil && notif.Status.InReplyToAccountID == nil {
log.Debugf(`post passed reply check (%v)`, notif.Status.ID)
ok := false
for _, subscriber := range subscribers {
if subscriber.ID == notif.Account.ID {
ok = true
break
}
}
if ok && notif.Status.Visibility == "public" {
log.Debugf("post %v is public and user has subscription", notif.Status.ID)
for _, regex := range whitelistRegexes {
if regex.MatchString(notif.Account.Acct) {
_, err = masto_client.Reblog(ctx, notif.Status.ID)
if err != nil {
log.Error(err)
}
log.Debugf("post %v belongs to whitelisted user (%v) by regex: %v", notif.Status.ID, notif.Account.Acct, regex.String())
continue notif_loop
}
}
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(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",
})
}
}
}
}