// Copyright 2025 eat-pray-ai & OpenWaygate // SPDX-License-Identifier: Apache-2.0 package commentThread import ( "errors" "io" "github.com/eat-pray-ai/yutu/pkg/common" "github.com/jedib0t/go-pretty/v6/table" "google.golang.org/api/youtube/v3" ) var ( errGetCommentThread = errors.New("failed to get comment thread") errInsertCommentThread = errors.New("failed to insert comment thread") ) type CommentThread struct { common.Fields AuthorChannelId string `yaml:"author_channel_id" json:"author_channel_id,omitempty"` ModerationStatus string `yaml:"moderation_status" json:"moderation_status,omitempty"` Order string `yaml:"order" json:"order,omitempty"` SearchTerms string `yaml:"search_terms" json:"search_terms,omitempty"` TextFormat string `yaml:"text_format" json:"text_format,omitempty"` TextOriginal string `yaml:"text_original" json:"text_original,omitempty"` VideoId string `yaml:"video_id" json:"video_id,omitempty"` AllThreadsRelatedToChannelId string `yaml:"all_threads_related_to_channel_id" json:"all_threads_related_to_channel_id,omitempty"` } type ICommentThread[T any] interface { Get() ([]*T, error) List(io.Writer) error Insert(io.Writer) error } type Option func(*CommentThread) func NewCommentThread(opts ...Option) ICommentThread[youtube.CommentThread] { c := &CommentThread{Fields: common.Fields{}} for _, opt := range opts { opt(c) } return c } func (c *CommentThread) Get() ([]*youtube.CommentThread, error) { if err := c.EnsureService(); err != nil { return nil, err } call := c.Service.CommentThreads.List(c.Parts) if len(c.Ids) > 0 { call = call.Id(c.Ids...) } if c.AllThreadsRelatedToChannelId != "" { call = call.AllThreadsRelatedToChannelId(c.AllThreadsRelatedToChannelId) } if c.ChannelId != "" { call = call.ChannelId(c.ChannelId) } if c.ModerationStatus != "" { call = call.ModerationStatus(c.ModerationStatus) } if c.Order != "" { call = call.Order(c.Order) } if c.SearchTerms != "" { call = call.SearchTerms(c.SearchTerms) } if c.TextFormat != "" { call = call.TextFormat(c.TextFormat) } if c.VideoId != "" { call = call.VideoId(c.VideoId) } return common.Paginate( &c.Fields, call, func(r *youtube.CommentThreadListResponse) ([]*youtube.CommentThread, string) { return r.Items, r.NextPageToken }, errGetCommentThread, ) } func (c *CommentThread) List(writer io.Writer) error { commentThreads, err := c.Get() if err != nil && commentThreads == nil { return err } common.PrintList( c.Output, commentThreads, writer, table.Row{"ID", "Author", "Video ID", "Text Display"}, func(cot *youtube.CommentThread) table.Row { snippet := cot.Snippet.TopLevelComment.Snippet return table.Row{cot.Id, snippet.AuthorDisplayName, snippet.VideoId, snippet.TextDisplay} }, ) return err } func (c *CommentThread) Insert(writer io.Writer) error { if err := c.EnsureService(); err != nil { return err } ct := &youtube.CommentThread{ Snippet: &youtube.CommentThreadSnippet{ ChannelId: c.ChannelId, TopLevelComment: &youtube.Comment{ Snippet: &youtube.CommentSnippet{ AuthorChannelId: &youtube.CommentSnippetAuthorChannelId{ Value: c.AuthorChannelId, }, ChannelId: c.ChannelId, TextOriginal: c.TextOriginal, VideoId: c.VideoId, }, }, }, } res, err := c.Service.CommentThreads.Insert([]string{"snippet"}, ct).Do() if err != nil { return errors.Join(errInsertCommentThread, err) } common.PrintResult( c.Output, res, writer, "CommentThread inserted: %s\n", res.Id, ) return nil } func WithAllThreadsRelatedToChannelId(allThreadsRelatedToChannelId string) Option { return func(c *CommentThread) { c.AllThreadsRelatedToChannelId = allThreadsRelatedToChannelId } } func WithAuthorChannelId(authorChannelId string) Option { return func(c *CommentThread) { c.AuthorChannelId = authorChannelId } } func WithModerationStatus(moderationStatus string) Option { return func(c *CommentThread) { c.ModerationStatus = moderationStatus } } func WithOrder(order string) Option { return func(c *CommentThread) { c.Order = order } } func WithSearchTerms(searchTerms string) Option { return func(c *CommentThread) { c.SearchTerms = searchTerms } } func WithTextFormat(textFormat string) Option { return func(c *CommentThread) { c.TextFormat = textFormat } } func WithTextOriginal(textOriginal string) Option { return func(c *CommentThread) { c.TextOriginal = textOriginal } } func WithVideoId(videoId string) Option { return func(c *CommentThread) { c.VideoId = videoId } } var ( WithParts = common.WithParts[*CommentThread] WithOutput = common.WithOutput[*CommentThread] WithService = common.WithService[*CommentThread] WithIds = common.WithIds[*CommentThread] WithMaxResults = common.WithMaxResults[*CommentThread] WithChannelId = common.WithChannelId[*CommentThread] )