github.com/Ptt-official-app/go-bbs@v0.12.0/pttbbs/pttbbs_write_article_connector.go (about) 1 package pttbbs 2 3 import ( 4 "fmt" 5 "math/rand" 6 "os" 7 "time" 8 9 "github.com/Ptt-official-app/go-bbs" 10 "github.com/Ptt-official-app/go-bbs/filelock" 11 ) 12 13 // CreateBoardArticleFilename get available filename for board with boardID, it will test is this filename not exist 14 // And open a file to occupy this filename 15 // Please see fhdr_stamp in pttbbs fhdr_stamp.c also 16 func (c *Connector) CreateBoardArticleFilename(boardID string) (filename string, err error) { 17 var f *os.File 18 for { 19 dtime := time.Now().Unix() 20 // TOOD: Check 2038 Problem 21 filename = fmt.Sprintf("M.%d.A.%3.3X", dtime, rand.Intn(0x1000)) 22 path, err := c.GetBoardArticleFilePath(boardID, filename) 23 if err != nil { 24 return filename, err 25 } 26 27 f, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) 28 if err == nil { 29 break 30 } 31 32 // TODO: Should log if can not lock file in first time, is system loading too heavy? 33 34 } 35 f.Close() 36 return 37 38 } 39 40 // NewArticleRecord returns a new ArticleRecord given a filename, owner, date, title 41 func (c *Connector) NewArticleRecord(filename, owner, date, title string) (bbs.ArticleRecord, error) { 42 ret := &FileHeader{ 43 filename: filename, 44 owner: owner, 45 date: date, 46 title: title, 47 } 48 return ret, nil 49 } 50 51 func (c *Connector) WriteBoardArticleFile(path string, content []byte) error { 52 53 f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644) 54 if err != nil { 55 return fmt.Errorf("openfile: %w", err) 56 57 } 58 defer f.Close() 59 err = filelock.Lock(f) 60 if err != nil { 61 // File is locked 62 return fmt.Errorf("filelock lock: %w", err) 63 } 64 defer filelock.Unlock(f) 65 66 if _, err := f.Write(content); err != nil { 67 return err 68 } 69 return nil 70 71 } 72 73 func (c *Connector) NewArticleRecordWithMap(args map[string]interface{}) (bbs.ArticleRecord, error) { 74 75 record := NewFileHeader() 76 77 owner, ok := args["owner"].(string) 78 if !ok { 79 return nil, fmt.Errorf("NewArticleRecord: owner must not be empty") 80 } 81 record.SetOwner(owner) 82 83 date, ok := args["date"].(string) 84 if !ok { 85 return nil, fmt.Errorf("NewArticleRecord: date must not be empty") 86 } 87 record.SetDate(date) 88 89 title, ok := args["title"].(string) 90 if !ok { 91 return nil, fmt.Errorf("NewArticleRecord: title must not be empty") 92 } 93 record.SetTitle(title) 94 95 boardID, ok := args["board_id"].(string) 96 if !ok { 97 return nil, fmt.Errorf("NewArticleRecord: board_id must not be empty") 98 } 99 100 filename := "" 101 dtime := time.Now().Unix() 102 rand.Seed(dtime) 103 104 var f *os.File 105 for { 106 filename = fmt.Sprintf("M.%d.A.%3.3X", dtime, rand.Intn(0x1000)) 107 path, err := c.GetBoardArticleFilePath(boardID, filename) 108 if err != nil { 109 return nil, err 110 } 111 112 f, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) 113 if err == nil { 114 break 115 } 116 } 117 defer f.Close() 118 119 record.SetFilename(filename) 120 121 err := filelock.Lock(f) 122 if err != nil { 123 // File is locked 124 return nil, err 125 } 126 defer filelock.Unlock(f) 127 128 data := fmt.Sprintf("作者: %s 看板: %s\n標題: %s \n時間: %s\n", 129 owner, boardID, title, date) 130 131 if _, err := f.Write([]byte(data)); err != nil { 132 return nil, err 133 } 134 135 return record, nil 136 } 137 138 func (c *Connector) AddArticleRecordFileRecord(name string, article bbs.ArticleRecord) error { 139 a, ok := article.(*FileHeader) 140 if !ok { 141 return fmt.Errorf("article should be create with NewArticleRecord") 142 } 143 return AppendFileHeaderFileRecord(name, a) 144 }