{"id":37225832,"url":"https://github.com/thecsw/mira","last_synced_at":"2026-01-15T01:50:18.945Z","repository":{"id":35148119,"uuid":"186197541","full_name":"thecsw/mira","owner":"thecsw","description":"The fantastic Golang Reddit API wrapper for gophers","archived":false,"fork":false,"pushed_at":"2024-07-23T01:56:08.000Z","size":371,"stargazers_count":63,"open_issues_count":0,"forks_count":12,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-07-23T04:37:23.808Z","etag":null,"topics":["api","go","golang","hacktoberfest","reddit","wrapper"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thecsw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2019-05-12T01:18:41.000Z","updated_at":"2024-07-23T01:56:09.000Z","dependencies_parsed_at":"2024-06-18T18:41:00.220Z","dependency_job_id":"38b46e75-ec4e-47e9-b81e-8bc72eed05de","html_url":"https://github.com/thecsw/mira","commit_stats":null,"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/thecsw/mira","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecsw%2Fmira","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecsw%2Fmira/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecsw%2Fmira/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecsw%2Fmira/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thecsw","download_url":"https://codeload.github.com/thecsw/mira/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecsw%2Fmira/sbom","scorecard":{"id":878263,"data":{"date":"2025-08-11","repo":{"name":"github.com/thecsw/mira","commit":"1301b29dd6d41d1c5f976fecc642c4c1a848cf8f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"name":"Code-Review","score":1,"reason":"Found 2/18 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/thecsw/mira/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/thecsw/mira/go.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-24T06:56:21.930Z","repository_id":35148119,"created_at":"2025-08-24T06:56:21.930Z","updated_at":"2025-08-24T06:56:21.930Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28441031,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T00:55:22.719Z","status":"ssl_error","status_checked_at":"2026-01-15T00:55:20.945Z","response_time":107,"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":["api","go","golang","hacktoberfest","reddit","wrapper"],"created_at":"2026-01-15T01:50:18.262Z","updated_at":"2026-01-15T01:50:18.934Z","avatar_url":"https://github.com/thecsw.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![mira](./mira.png)\n\n\u003cdiv id='badges' align='center'\u003e\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/thecsw/mira/v4)](https://goreportcard.com/report/github.com/thecsw/mira/v4)\n[![GoDoc](https://godoc.org/github.com/thecsw/mira/v4?status.svg)](https://godoc.org/github.com/thecsw/mira/v4)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n\n\u003c/div\u003e\n\nFor full documentation, please see the [Godoc page](https://godoc.org/github.com/thecsw/mira/v4)\n\n*mira* is a Reddit Api Wrapper written in beautiful Go. Featured in issue [306](https://golangweekly.com/issues/306) of Golang Weekly 🚀\n\nIt is super simple to use the bot as we also provide you with simple but fully extensive \ninterfaces. Currently, *mira* is a project that is considered more or less complete. All\nmain functionality, such as streaming, data manipulation, data request, submissions, links,\netc. are fully implemented. *mira* can be extended to use any Reddit API endpoint. More \ndetails at the bottom of this page.\n\n## Demo\n\n![Demo](./examples/demo/demo.svg)\n\nTwo quick notes: all actions should be done via `Reddit` struct, I thought it would make it\nsimpler to work with. Secondly, all actions require the objects full `thing_id`, so you have\nto use `GetId()` to get that id. Every struct has that method implemented and it will return\na string in the form of `t[1-6]_[a-z0-9]{5}`. Refer to the following table for the classifications\nof the structs.\n\n**Type Prefixes**\n\n| Prefix | Type                             |\n|--------|----------------------------------|\n|   t1   | Comment                          |\n|   t2   | Redditor                         |\n|   t3   | Submission, PostListing contents |\n|   t4   | Message (NOT IMPLEMENTED)        |\n|   t5   | Subreddit                        |\n|   t6   | Award (NOT IMPLEMENTED)          |\n\n## Config file\n\nThe config file structure is very simple:\n\n```\nlogin.conf\n----------\nCLIENT_ID =\nCLIENT_SECRET =\nUSERNAME =\nPASSWORD =\nUSER_AGENT =\n```\n\n``` go\nr, err := mira.Init(mira.ReadCredsFromFile(\"login.conf\"))\n```\n\n## Environment setup\n\nMira also works with environmental variables, here is an example from docker-compose\n\n```\n    environment:\n      - BOT_CLIENT_ID=hunteoahtnhnt432\n      - BOT_CLIENT_SECRET=ehoantehont4ht34hnt332\n      - BOT_USER_AGENT='u/mytestbot developed by thecsw'\n      - BOT_USERNAME=mytestbot\n      - BOT_PASSWORD=verygoodpassword\n```\n\nAnd the login will look like this:\n\n``` go\nr, err := mira.Init(mira.ReadCredsFromEnv())\n```\n\nOr you can always just fill in the values directly.\n\n## Examples\n\nNote: Error checking is omitted for brevity.\n\n### Streaming\n\nStreaming new submissions is very simple! *mira* supports streaming comment replies, \nmentions, new subreddit's/redditor's comments, and new subreddit's/redditor's submissions.\n\n``` go\n// r is an instance of *mira.Reddit\nr, err := mira.Init(mira.ReadCredsFromFile(\"login.conf\"))\n\n// Start streaming my comment replies\nc, err := r.StreamCommentReplies()\nfor {\n\tmsg := \u003c-c\n\tr.Comment(msg.GetId()).Reply(\"I got your message!\")\n}\n\n// Start streaming my mentions\n// Start streaming my comment replies\nc, err := r.StreamMentions()\nfor {\n\tmsg := \u003c-c\n\tr.Comment(msg.GetId()).Reply(\"I got your mention of me!\")\n}\n\n// Start streaming subreddits' submissions\nc, err := r.Subreddit(\"tifu\", \"wholesomememes\").StreamSubmissions()\nfor {\n\tpost := \u003c-c\n\tr.Submission(post.GetId()).Save(\"hello there\")\n}\n\n// NOTE: Second value is the stop channel. Send a true value\n// to the stop channel and the goroutine will return. \n// Basically, `stop \u003c- true`\n\n// Start streaming subreddits' comments\nc, err := r.Subreddit(\"all\").StreamComments()\nfor {\n\tmsg := \u003c-c\n\tr.Comment(msg.GetId()).Reply(\"my reply!\")\n}\n\n// Start streaming redditor's submissions\nc, err := r.Redditor(\"thecsw\").StreamSubmissions()\nfor {\n\tpost := \u003c-c\n\tr.Submission(post.GetId()).Save(\"hello there\")\n}\n\t\n// Start streaming redditor' comments\nc, err := r.Redditor(\"thecsw\").StreamComments()\nfor {\n\tmsg := \u003c-c\n\tr.Comment(msg.GetId()).Reply(\"my reply!\")\n}\n```\n\n### Submitting, Commenting, Replying, and Editing\n\nIt is very easy to post a submission, comment on it, reply to a message, or\nedit a comment.\n\n``` go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/thecsw/mira/v4\"\n)\n\n// Error checking is omitted for brevity\nfunc main() {\n\tr, err := mira.Init(mira.ReadCredsFromFile(\"login.conf\"))\n\n\t// Make a submission\n\tpost, err := r.Subreddit(\"mysubreddit\").Submit(\"mytitle\", \"mytext\")\n\n\t// Comment on our new submission\n\tcomment, err := r.Submission(post.GetId()).Save(\"mycomment\")\n\n\t// Reply to our own comment\n\treply, err := r.Comment(comment.GetId()).Reply(\"myreply\")\n\n\t// Delete the reply\n\tr.Comment(reply.GetId()).Delete()\n\n\t// Edit the first comment\n\tnewComment, err := r.Comment(comment.GetId()).Edit(\"myedit\")\n\n\t// Show the comment's body\n\tfmt.Println(newComment.GetBody())\n}\n```\n\n### Composing a message\n\nWe can also send a message to another user!\n\n``` go\npackage main\n\nimport (\n\t\"github.com/thecsw/mira/v4\"\n)\n\nfunc main() {\n\tr, err := mira.Init(mira.ReadCredsFromFile(\"login.conf\"))\n\n\tr.Redditor(\"myuser\").Compose(\"mytitle\", \"mytext\")\n}\n```\n\n### Going through hot, new, top, rising, controversial, and random\n\nYou can also traverse through a number of submissions using\none of our methods.\n\n``` go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/thecsw/mira/v4\"\n)\n\nfunc main() {\n\tr, err := mira.Init(mira.ReadCredsFromFile(\"login.conf\"))\n\tsort := \"top\"\n\tvar limit int = 25\n\tduration := \"all\"\n\tsubs, err := r.Subreddit(\"all\").Submissions(sort, duration, limit)\n\tfor _, v := range subs {\n\t\tfmt.Println(\"Submission Title: \", v.GetTitle())\n\t}\n}\n```\n\n### Getting reddit info\n\nYou can extract info from any reddit ID using mira. The returned value is an \ninstance of mira.MiraInterface.\n\n``` go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/thecsw/mira/v4\"\n)\n\nfunc main() {\n\tr, err := mira.Init(mira.ReadCredsFromFile(\"login.conf\"))\n\tme, err := r.Me().Info()\n\tcomment, err := r.Comment(\"t1_...\").Info()\n\tredditor, err := r.Redditor(\"t2_...\").Info()\n\tsubmission, err := r.Submission(\"t3_...\").Info()\n\tsubreddit, err := r.Subreddit(\"t5_...\").Info()\n}\n```\n\nHere is the interface:\n\n``` go\ntype MiraInterface interface {\n\tGetId() string\n\tGetParentId() string\n\tGetTitle() string\n\tGetBody() string\n\tGetAuthor() string\n\tGetName() string\n\tGetKarma() float64\n\tGetUps() float64\n\tGetDowns() float64\n\tGetSubreddit() string\n\tGetCreated() float64\n\tGetFlair() string\n\tGetUrl() string\n\tIsRoot() bool\n}\n```\n\n## Mira Caller\n\nSurely, Reddit API is always developing and I can't implement all endpoints. It will be a bit of a bloat.\nInstead, you have accessto *Reddit.MiraRequest method that will let you to do any custom reddit api calls!\n\nHere is the signature:\n\n``` go\nfunc (c *Reddit) MiraRequest(method string, target string, payload map[string]string) ([]byte, error) {...}\n```\n\nIt is pretty straight-forward. The return is a slice of bytes. Parse it yourself.\n\nHere is an example of how Reddit.Reply() uses MiraRequest:\n\nNOTE: `checkType(...)` is a quick method to pop a value from the\nqueue and make sure it's a valid value and type. For example,\n\n``` go\nr.Comment(\"COMM1\").Submission(\"SUBM1\").Redditor(\"USER1\")\n```\n\nwill add elements to its internal queue, so that the layout is:\n\n```\nEnqueue-\u003e\n                  redditor  submission  comment               // type\n        |BACK| -\u003e |USER1| -\u003e |SUBM1| -\u003e |COMM1| -\u003e |FRONT|    // value\n                                                  Dequeue-\u003e\n```\n\nSo that when you run `r.checkType(\"comment\")`, it will dequeue `COMM1`\nand return triplet `\"COMM1\", \"comment\", nil`.\n\nIf you run `r.checkType(\"redditor\")` (will fail because subm is at the end),\nyou will get `\"\", \"\", \"errors.New(\"the passed type...\")`\n\nHere is an example of how you check that the last element to dequeue is\na type that you're expecting:\n\n``` go\nfunc (c *Reddit) Reply(text string) (models.CommentWrap, error) {\n\tret := \u0026models.CommentWrap{}\n\t// Second return is type, which is \"comment\"\n\tname, _, err := c.checkType(\"comment\")\n\tif err != nil {\n\t\treturn *ret, err\n\t}\n\ttarget := RedditOauth + \"/api/comment\"\n\tans, err := c.MiraRequest(\"POST\", target, map[string]string{\n\t\t\"text\":     text,\n\t\t\"thing_id\": name,\n\t\t\"api_type\": ApiTypeJson,\n\t})\n\tjson.Unmarshal(ans, ret)\n\treturn *ret, err\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthecsw%2Fmira","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthecsw%2Fmira","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthecsw%2Fmira/lists"}