post.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package kinds
  2. import (
  3. "net/url"
  4. "strings"
  5. "time"
  6. "mimicry/style"
  7. "errors"
  8. "mimicry/render"
  9. "mimicry/ansi"
  10. )
  11. type Post Dict
  12. // TODO: go through and remove all the trims, they
  13. // make things less predictable
  14. // TODO: make the Post references *Post because why not
  15. func (p Post) Kind() (string, error) {
  16. kind, err := Get[string](p, "type")
  17. return strings.ToLower(kind), err
  18. }
  19. func (p Post) Title() (string, error) {
  20. title, err := GetNatural(p, "name", "en")
  21. return strings.TrimSpace(title), err
  22. }
  23. func (p Post) Body(width int) (string, error) {
  24. body, err := GetNatural(p, "content", "en")
  25. if err != nil {
  26. return "", err
  27. }
  28. mediaType, err := Get[string](p, "mediaType")
  29. if err != nil {
  30. mediaType = "text/html"
  31. }
  32. return render.Render(body, mediaType, width)
  33. }
  34. // func (p Post) BodyPreview() (string, error) {
  35. // body, err := p.Body()
  36. // // probably should convert to runes and just work with that
  37. // if len(body) > 280*2 { // this is a bug because len counts bytes whereas later I work based on runes
  38. // return fmt.Sprintf("%s…", string([]rune(body)[:280])), err
  39. // } else {
  40. // return body, err
  41. // }
  42. // }
  43. func (p Post) Identifier() (*url.URL, error) {
  44. return GetURL(p, "id")
  45. }
  46. func (p Post) Created() (time.Time, error) {
  47. return GetTime(p, "published")
  48. }
  49. func (p Post) Updated() (time.Time, error) {
  50. return GetTime(p, "updated")
  51. }
  52. func (p Post) Category() string {
  53. return "post"
  54. }
  55. func (p Post) Creators() ([]Actor, error) {
  56. return GetContent[Actor](p, "attributedTo")
  57. }
  58. func (p Post) Recipients() ([]Actor, error) {
  59. return GetContent[Actor](p, "to")
  60. }
  61. func (p Post) Attachments() ([]Link, error) {
  62. return GetLinksLenient(p, "attachment")
  63. }
  64. func (p Post) Link() (Link, error) {
  65. kind, err := p.Kind()
  66. if err != nil {
  67. return nil, err
  68. }
  69. links, err := GetLinksStrict(p, "url")
  70. if err != nil {
  71. return nil, err
  72. }
  73. switch kind {
  74. case "audio", "image", "video":
  75. return SelectBestLink(links, kind)
  76. case "article", "document", "note", "page":
  77. return SelectFirstLink(links)
  78. default:
  79. return nil, errors.New("Link extraction is not supported for type " + kind)
  80. }
  81. }
  82. func (p Post) header(width int) (string, error) {
  83. output := ""
  84. if title, err := p.Title(); err == nil {
  85. output += style.Bold(title) + "\n"
  86. }
  87. if kind, err := p.Kind(); err == nil {
  88. output += style.Color(kind)
  89. }
  90. if creators, err := p.Creators(); err == nil {
  91. names := []string{}
  92. for _, creator := range creators {
  93. if name, err := creator.InlineName(); err == nil {
  94. names = append(names, style.Link(name))
  95. }
  96. }
  97. if len(names) > 0 {
  98. output += " by " + strings.Join(names, ", ")
  99. }
  100. }
  101. if recipients, err := p.Recipients(); err == nil {
  102. names := []string{}
  103. for _, recipient := range recipients {
  104. if name, err := recipient.InlineName(); err == nil {
  105. names = append(names, style.Link(name))
  106. }
  107. }
  108. if len(names) > 0 {
  109. output += " to " + strings.Join(names, ", ")
  110. }
  111. }
  112. if created, err := p.Created(); err == nil {
  113. output += " at " + style.Color(created.Format("3:04 pm"))
  114. output += " on " + style.Color(created.Format("2 Jan 2006"))
  115. }
  116. return ansi.Wrap(output, width), nil
  117. }
  118. func (p Post) String() (string, error) {
  119. output := ""
  120. width := 100
  121. if header, err := p.header(width - 2); err == nil {
  122. output += ansi.Indent(header, " ", true)
  123. output += "\n\n"
  124. }
  125. if body, err := p.Body(width - 4); err == nil {
  126. output += ansi.Indent(body, " ", true)
  127. output += "\n\n"
  128. }
  129. if attachments, err := p.Attachments(); err == nil {
  130. if len(attachments) > 0 {
  131. section := "Attachments:\n"
  132. names := []string{}
  133. for _, attachment := range attachments {
  134. if name, err := attachment.String(); err == nil {
  135. names = append(names, style.Link(name))
  136. }
  137. }
  138. section += ansi.Indent(ansi.Wrap(strings.Join(names, "\n"), width - 4), " ", true)
  139. section = ansi.Indent(ansi.Wrap(section, width - 2), " ", true)
  140. output += section
  141. output += "\n"
  142. }
  143. }
  144. return output, nil
  145. }