GraphQL API and utilities for the rpdata project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
2.3 KiB

  1. package forumlog
  2. import (
  3. "bufio"
  4. "fmt"
  5. "strings"
  6. "time"
  7. "git.aiterp.net/rpdata/api/internal/importers/mirclike"
  8. "git.aiterp.net/rpdata/api/models"
  9. )
  10. // A ParsedLog contains the parsed log header and its posts.
  11. type ParsedLog struct {
  12. Log models.Log
  13. Posts []models.Post
  14. }
  15. // ParseLogs parses the logs from the data.
  16. func ParseLogs(data string, tz *time.Location) ([]ParsedLog, error) {
  17. metadata := ParseMetadata(data)
  18. results := make([]ParsedLog, 0, len(metadata["Date"]))
  19. scanner := bufio.NewScanner(strings.NewReader(data))
  20. for i, dateStr := range metadata["Date"] {
  21. // Parse date
  22. date, err := time.ParseInLocation("January 2, 2006", dateStr, tz)
  23. if err != nil {
  24. return nil, fmt.Errorf("Failed to parse date #%d: %#+v is not the in the correct format of \"January 2, 2006\"", i+1, dateStr)
  25. }
  26. // Parse posts
  27. posts := make([]models.Post, 0, 128)
  28. parsing := false
  29. prev := ""
  30. prevPost := models.Post{}
  31. for scanner.Scan() {
  32. line := strings.Trim(scanner.Text(), "\r\t  ")
  33. // Skip lines with links to other logs using the --> and <-- notation.
  34. if strings.HasPrefix(line, "-") || strings.HasPrefix(line, "<") {
  35. prev = line
  36. continue
  37. }
  38. // If parsing and reaching a double empty-line, the session is done.
  39. if parsing && len(line) < 2 && len(prev) < 2 {
  40. break
  41. }
  42. // Skip empty lines, but record them as thep revious for the above check.
  43. if len(line) < 2 {
  44. prev = line
  45. continue
  46. }
  47. // If not parsing, skip until the first mirclike post.
  48. if !parsing {
  49. if strings.HasPrefix(line, "[") {
  50. parsing = true
  51. } else {
  52. prev = line
  53. continue
  54. }
  55. }
  56. // Parse the post.
  57. post, err := mirclike.ParsePost(line, date, prevPost)
  58. if err != nil {
  59. summary := ""
  60. for _, ru := range line {
  61. summary += string(ru)
  62. if len(summary) > 60 {
  63. summary += "..."
  64. break
  65. }
  66. }
  67. return nil, fmt.Errorf("Failed to parse post: %s", summary)
  68. }
  69. posts = append(posts, post)
  70. prevPost = post
  71. prev = line
  72. }
  73. // No posts means there's a problem.
  74. if len(posts) == 0 {
  75. return nil, fmt.Errorf("Session %d (%s) has no posts (too many dates?)", i+1, dateStr)
  76. }
  77. // Add it.
  78. results = append(results, ParsedLog{
  79. Log: models.Log{Date: posts[0].Time},
  80. Posts: posts,
  81. })
  82. }
  83. return results, nil
  84. }