package services import ( "CatsOfMastodonBotGo/internal/database" "CatsOfMastodonBotGo/internal/domain" "context" "encoding/json" "fmt" "net/http" "strings" "gorm.io/gorm" "gorm.io/gorm/clause" ) type PostService struct { db *gorm.DB } var PostServiceInstance *PostService // Constructor func InitPostService() { PostServiceInstance = &PostService{db: database.Gorm} } func (*PostService) GetPostsFromApi(ctx context.Context, tag string, instance string) (error, []domain.Post) { var requestUrl = instance + "/api/v1/timelines/tag/" + tag + "?limit=40" req, err := http.NewRequestWithContext(ctx, "GET", requestUrl, nil) if err != nil { return err, nil } resp, err := http.DefaultClient.Do(req) if err != nil { return err, nil } if resp.StatusCode != 200 || strings.Split(strings.ToLower(resp.Header.Get("Content-Type")), ";")[0] != "application/json" { return fmt.Errorf("Status code:", resp.StatusCode, " Content-Type:", resp.Header.Get("Content-Type")), nil } var posts []domain.Post = nil err = json.NewDecoder(resp.Body).Decode(&posts) if err != nil { return err, nil } // defer: it basically means "do this later when the function returns" defer resp.Body.Close() if posts == nil { return fmt.Errorf("no posts found for tag %s on instance %s", tag, instance), nil } return nil, posts } func (ps *PostService) GetExistingPostIds() []string { var existingPostIds []string ps.db.Model(&domain.Post{}).Pluck("id", &existingPostIds) return existingPostIds } func (ps *PostService) GetExistingAccountIds() []string { var existingAccountIds []string ps.db.Model(&domain.Account{}).Pluck("acc_id", &existingAccountIds) return existingAccountIds } func (*PostService) GetNewPosts(existingPostIds []string, posts []domain.Post) []domain.Post { var newPosts []domain.Post = nil for _, post := range posts { if !arrayContains(existingPostIds, post.ID) && len(post.Attachments) > 0 && !post.Account.IsBot { var allImageMedia = true for _, attachment := range post.Attachments { if attachment.Type != "image" { allImageMedia = false break } } // Inefficient but anyways if allImageMedia { newPosts = append(newPosts, post) } } } return newPosts } func (*PostService) GetNewAccounts(existingAccountIds []string, posts []domain.Post) []domain.Account { var newAccounts []domain.Account = nil for _, post := range posts { if !arrayContains(existingAccountIds, post.Account.AccId) { newAccounts = append(newAccounts, post.Account) } } return newAccounts } func (ps *PostService) InsertNewPosts(newPosts []domain.Post) int { return int(ps.db.Create(&newPosts).RowsAffected) } func (ps *PostService) InsertNewAccounts(newAccounts []domain.Account) int { return int(ps.db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&newAccounts).RowsAffected) } // From this point on, its for the api endpoints func (ps *PostService) GetRandomPost() domain.Post { var post domain.Post ps.db. Preload("Account"). Preload("Attachments", "approved = ?", true). Order("RANDOM()"). First(&post) if len(post.Attachments) > 0 { post.Attachments = []domain.MediaAttachment{post.Attachments[0]} } return post } func (ps *PostService) ApproveMedia(mediaId string) bool { return ps.db.Model(&domain.MediaAttachment{}). Where("id = ?", mediaId). Update("approved", true).RowsAffected > 0 } func (ps *PostService) RejectMedia(mediaId string) bool { return ps.db.Model(&domain.MediaAttachment{}). Where("id = ?", mediaId). Update("rejected", true).RowsAffected > 0 } // Get a post which approve and rejet are false (For admin panel) func (ps *PostService) GetMedia() domain.MediaAttachment { var media domain.MediaAttachment ps.db.Model(&domain.MediaAttachment{}). Where("approved = ?", false). Where("rejected = ?", false). Order("RANDOM()"). First(&media) return media } func arrayContains(arr []string, str string) bool { for _, a := range arr { if a == str { return true } } return false }