{"id":41699887,"url":"https://github.com/rorycl/letters","last_synced_at":"2026-01-24T20:57:24.663Z","repository":{"id":332840220,"uuid":"929563954","full_name":"rorycl/letters","owner":"rorycl","description":"Email parsing for go. This fork of github.com/mnako/letters focuses on modularity and speed","archived":false,"fork":false,"pushed_at":"2026-01-15T19:13:25.000Z","size":648,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-15T21:45:33.873Z","etag":null,"topics":["email","golang","mime","parse","parser","rfc2045","rfc5322"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rorycl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-08T20:51:36.000Z","updated_at":"2026-01-15T19:13:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rorycl/letters","commit_stats":null,"previous_names":["rorycl/letters"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/rorycl/letters","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rorycl%2Fletters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rorycl%2Fletters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rorycl%2Fletters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rorycl%2Fletters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rorycl","download_url":"https://codeload.github.com/rorycl/letters/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rorycl%2Fletters/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28736793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T19:23:36.361Z","status":"ssl_error","status_checked_at":"2026-01-24T19:23:28.966Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["email","golang","mime","parse","parser","rfc2045","rfc5322"],"created_at":"2026-01-24T20:57:24.065Z","updated_at":"2026-01-24T20:57:24.650Z","avatar_url":"https://github.com/rorycl.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Letters\nEmail parsing for go.\n\nv0.1.3 : 01 May 2025 : allow Parser to be shared.\n\nThis is a fork of [mnako/letters](https://github.com/mnako/letters), a\nminimalistic Golang library for parsing plain and MIME emails. Thanks to\n@mnako and contributors, letters has great support for languages other\nthan English, text and transfer encodings.\n\nThis fork (at [#9d1a5, v0.2.3](https://github.com/mnako/letters/commit/db1b793c8119d8ed3d575f44df4391262829d1a5))\nof letters focuses on extensibility through modularisation as set out in\n[PR #124](https://github.com/mnako/letters/pull/124). This library also\nprovides improved performance and customisation through user-defined\nfuncs, for example for efficient inline and attachment file processing.\n\nFuture plans include making parsing errors, such as for invalid email\naddresses, optionally non-fatal.\n\nSee [mailboxoperator](https://github.com/rorycl/mailboxoperator) for a\nconvenient way of iterating over emails in mailbox or maildir format. An\nexample client for mailboxoperator is\n[mailfinder](https://github.com/rorycl/mailfinder), which also uses this\nmodule.\n\n## Quickstart\n\nInstall\n\n```\ngo get github.com/rorycl/letters@latest\n```\n\nParse an email:\n\n```go\nreader := someEmailIOReaderProvider() // see mailboxoperator\np := letters.NewParser()\nparsedEmail, err := p.Parse(reader)\n\n// \u0026email.Email{\n// \tHeaders: email.Headers{\n// \t\tDate: time.Time(time.Date(2019, 4, 1, 0, 55, 0, 0, time.UTC)),\n// \t\tSender: \u0026mail.Address{\n// \t\t\tName:    \"อลิซ ผู้ส่งจดหมาย\",\n// \t\t\tAddress: \"alis.phusngcdhmay@example.com\",\n// \t\t},\n// \t\tFrom: []*mail.Address{\n// \t\t\t\u0026mail.Address{\n// \t\t\t\tName:    \"อลิซ ผู้ส่งจดหมาย\",\n// \t\t\t\tAddress: \"alis.phusngcdhmay@example.com\",\n// \t\t\t},\n// \t\t},\n// \t\tTo: []*mail.Address{\n// \t\t\t\u0026mail.Address{\n// \t\t\t\tName:    \"บ๊อบ ผู้รับ\",\n// \t\t\t\tAddress: \"bob.phurab@example.com\",\n// \t\t\t},\n// \t\t},\n// \t\tCc: []*mail.Address{\n// \t\t\t\u0026mail.Address{\n// \t\t\t\tName:    \"แดน ผู้รับ\",\n// \t\t\t\tAddress: \"dan.phurab@example.com\",\n// \t\t\t},\n// \t\t},\n// \t\tMessageID: \"Message-Id-1@example.com\",\n// \t\tInReplyTo: []string{\n// \t\t\t\"Message-Id-0@example.com\",\n// \t\t},\n// \t\tReferences: []string{\n// \t\t\t\"Message-Id-0@example.com\",\n// \t\t},\n// \t\tSubject:  \"📧 Test แพนแกรมภาษาไทย\",\n// \t\tComments: \"Message Header Comment\",\n// \t\tKeywords: []string{\n// \t\t\t\"Keyword 1\",\n// \t\t\t\"Keyword 2\",\n// \t\t},\n// \t\tResentDate: time.Time(time.Date(2019, 4, 1, 0, 55, 0, 0, time.UTC)),\n// \t\tExtraHeaders: map[string][]string{\n// \t\t\t\"X-Clacks-Overhead\": []string{\n// \t\t\t\t\"GNU Terry Pratchett\",\n// \t\t\t},\n// \t\t},\n// \t\tContentInfo: \u0026email.ContentInfo{\n// \t\t\tType: \"multipart/mixed\",\n// \t\t\tTypeParams: map[string]string{\n// \t\t\t\t\"boundary\": \"MixedBoundaryString\",\n// \t\t\t\t\"charset\":  \"tis-620\",\n// \t\t\t},\n// \t\t\tDisposition:       \"\",\n// \t\t\tDispositionParams: map[string]string(nil), // p0\n// \t\t\tTransferEncoding:  \"base64\",\n// \t\t\tID:                \"\",\n// \t\t\tCharset:           \"tis-620\",\n// \t\t},\n// \t\tReceived: nil,\n// \t},\n// \tText: \"\"\n// \tEnrichedText: \"\u003cbold\u003eเป็นมนุษย์สุดประเสริฐเลิศคุณค่า\u003c/bold\u003e ...\"\n// \tHTML: \"\"\n// \tFiles: []*email.File{\n// \t\t\u0026email.File{\n// \t\t\tFileType: \"inline\",\n// \t\t\tName:     \"inline-jpg-image-without-disposition.jpg\",\n// \t\t\tContentInfo: \u0026email.ContentInfo{\n// \t\t\t\tType: \"image/jpeg\",\n// \t\t\t\tTypeParams: map[string]string{\n// \t\t\t\t\t\"name\": \"inline-jpg-image-without-disposition.jpg\",\n// \t\t\t\t},\n// \t\t\t\tDisposition:       \"\",\n// \t\t\t\tDispositionParams: map[string]string(nil), // p0\n// \t\t\t\tTransferEncoding:  \"base64\",\n// \t\t\t\tID:                \"\",\n// \t\t\t\tCharset:           \"tis-620\",\n// \t\t\t},\n// \t\t\tData: []byte{\n// \t\t\t\t239, 191, 189, 224, 184, 184, 239, 191, 189, ...\n// \t\t\t},\n// \t\t},\n// \t\t},\n// \t\t\u0026email.File{\n// \t\t\tFileType: \"attachment\",\n// \t\t\tName:     \"attached-pdf-filename.pdf\",\n// \t\t\tContentInfo: \u0026email.ContentInfo{\n// \t\t\t\tType: \"application/pdf\",\n// \t\t\t\tTypeParams: map[string]string{\n// \t\t\t\t\t\"name\": \"attached-pdf-name.pdf\",\n// \t\t\t\t},\n// \t\t\t\tDisposition: \"attachment\",\n// \t\t\t\tDispositionParams: map[string]string{\n// \t\t\t\t\t\"filename\": \"attached-pdf-filename.pdf\",\n// \t\t\t\t},\n// \t\t\t\tTransferEncoding: \"base64\",\n// \t\t\t\tID:               \"\",\n// \t\t\t\tCharset:          \"tis-620\",\n// \t\t\t},\n// \t\t\tData: []byte{\n// \t\t\t\t37, 80, 68, 70, 45, 49, 46, 13, 116, 114, 97, ...\n// \t\t\t},\n// \t\t},\n// }\n```\n\n## Options\n\nVarious options are provided for customising the Parser, including:\n\n```go\n// skip content types\nfunc WithSkipContentTypes(skipContentTypes []string) Opt\n// provide a custom address processing function\nfunc WithCustomAddressFunc(af func(string) (*mail.Address, error)) Opt\n// provide a custom processing function for string lists of addresses\nfunc WithCustomAddressesFunc(af func(list string) ([]*mail.Address, error)) Opt\n// provide a custom date processing function\nfunc WithCustomDateFunc(df func(string) (time.Time, error)) Opt\n// provide a custom file processing function\nfunc WithCustomFileFunc(ff func(*email.File) error) Opt\n// save files to the stated directory (an example of WithCustomFileFunc)\nfunc WithSaveFilesToDirectory(dir string) Opt\n// only process headers\nfunc WithHeadersOnly() Opt\n// skip processing attachments\nfunc WithoutAttachments() Opt\n// show verbose processing info (currently a noop)\nfunc WithVerbose() Opt\n```\n\nMore than one option can be supplied.\n\nThe `WithoutAttachments` and `WithHeadersOnly` options determine if only part\nof an email will be processed.\n\nThe `WithSkipContentTypes` allows the user to skip processing MIME\nmessage parts with the supplied content-types.\n\nThe date and address \"With\" options allow the provision of custom funcs to\noverride the `net/mail` funcs normally used. For example it might be necessary\nto extend the date parsing capabilities to deal with poorly formatted date\nstrings produced by older SMTP servers.\n\nThe `WithCustomFileFunc` allows the provision of a custom func for saving,\nfiltering and/or processing of inline or attached files without reading them\nfirst into an `email.File.Data` []byte slice first, which is the default\nbehaviour. The `WithSaveFilesToDirectory` option is an example of such a custom\nfunc.\n\nAs shown in the [parser/optspkg_test.go](parser/optspkg_test.go) package\ntest, `WithCustomFileFunc` can be used to, for example, only process\n`image/jpeg` files. More examples are shown in\n[parser/opts_test.go](parser/opts_test.go), for example:\n\n```go\nopt := parser.WithHeadersOnly() // the headers only option\np := letters.NewParser(opt, parser.WithVerbose()) // options can be chained\nparsedEmail, err := p.Parse(emailReader)\nif err != nil {\n\treturn fmt.Errorf(\"error while parsing email headers: %s\", err)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frorycl%2Fletters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frorycl%2Fletters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frorycl%2Fletters/lists"}