{"id":13462987,"url":"https://github.com/tpitale/mail_room","last_synced_at":"2025-05-14T10:08:50.341Z","repository":{"id":6549022,"uuid":"7790606","full_name":"tpitale/mail_room","owner":"tpitale","description":"Forward mail from gmail IMAP to a callback URL or job worker, simply.","archived":false,"fork":false,"pushed_at":"2025-03-06T17:17:19.000Z","size":324,"stargazers_count":199,"open_issues_count":7,"forks_count":51,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-11T19:05:45.631Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/tpitale.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2013-01-24T04:38:48.000Z","updated_at":"2025-05-04T11:27:51.000Z","dependencies_parsed_at":"2023-02-17T04:40:27.534Z","dependency_job_id":"6a218aec-2bbf-438a-adb9-23536d6c22d6","html_url":"https://github.com/tpitale/mail_room","commit_stats":{"total_commits":212,"total_committers":19,"mean_commits":"11.157894736842104","dds":0.4716981132075472,"last_synced_commit":"481cec6c2bfdaac70a9900e44eef36f12a745260"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpitale%2Fmail_room","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpitale%2Fmail_room/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpitale%2Fmail_room/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpitale%2Fmail_room/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tpitale","download_url":"https://codeload.github.com/tpitale/mail_room/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254120161,"owners_count":22017952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-07-31T13:00:43.466Z","updated_at":"2025-05-14T10:08:50.287Z","avatar_url":"https://github.com/tpitale.png","language":"Ruby","funding_links":[],"categories":["Communication","Ruby"],"sub_categories":["E-Mail Processing"],"readme":"# mail_room #\n\nmail_room is a configuration based process that will listen for incoming\ne-mail and execute a delivery method when a new message is\nreceived. mail_room supports the following methods for receiving e-mail:\n\n* IMAP\n* [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/api/resources/mail-api-overview?view=graph-rest-1.0)\n\nExamples of delivery methods include:\n\n* POST to a delivery URL (Postback)\n* Queue a job to Sidekiq or Que for later processing (Sidekiq or Que)\n* Log the message or open with LetterOpener (Logger or LetterOpener)\n\n[![Build Status](https://travis-ci.org/tpitale/mail_room.png?branch=master)](https://travis-ci.org/tpitale/mail_room)\n[![Code Climate](https://codeclimate.com/github/tpitale/mail_room/badges/gpa.svg)](https://codeclimate.com/github/tpitale/mail_room)\n\n## Installation ##\n\nAdd this line to your application's Gemfile:\n\n    gem 'mail_room'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install mail_room\n\nYou will also need to install `faraday` or `letter_opener` if you use the `postback` or `letter_opener` delivery methods, respectively.\n\n## Usage ##\n\n    mail_room -c /path/to/config.yml\n\n**Note:** To ignore missing config file or missing `mailboxes` key, use `-q` or `--quiet`\n\n## Configuration ##\n\n```yaml\n---\n:mailboxes:\n  -\n    :email: \"user1@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :search_command: 'NEW'\n    :logger:\n      :log_path: /path/to/logfile/for/mailroom\n    :delivery_options:\n      :delivery_url: \"http://localhost:3000/inbox\"\n      :delivery_token: \"abcdefg\"\n      :content_type: \"text/plain\"\n\n  -\n    :email: \"user2@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: postback\n    :delivery_options:\n      :delivery_url: \"http://localhost:3000/inbox\"\n      :delivery_token: \"abcdefg\"\n  -\n    :email: \"user3@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: logger\n    :delivery_options:\n      :log_path: \"/var/log/user3-email.log\"\n  -\n    :email: \"user4@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: letter_opener\n    :delete_after_delivery: true\n    :expunge_deleted: true\n    :delivery_options:\n      :location: \"/tmp/user4-email\"\n  -\n    :email: \"user5@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: sidekiq\n    :delivery_options:\n      :redis_url: redis://localhost:6379\n      :worker: EmailReceiverWorker\n  -\n    :email: \"user6@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: sidekiq\n    :delivery_options:\n      # When pointing to sentinel, follow this sintax for redis URLs:\n      # redis://:\u003cpassword\u003e@\u003cmaster-name\u003e/\n      :redis_url: redis://:password@my-redis-sentinel/\n      :sentinels:\n        -\n          :host: 127.0.0.1\n          :port: 26379\n      :worker: EmailReceiverWorker\n  -\n    :email: \"user7@outlook365.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :inbox_method: microsoft_graph\n    :inbox_options:\n      :tenant_id: 12345\n      :client_id: ABCDE\n      :client_secret: YOUR-SECRET-HERE\n      :poll_interval: 60\n      :azure_ad_endpoint: https://login.microsoftonline.com\n      :graph_endpoint: https://graph.microsoft.com\n    :delivery_method: sidekiq\n    :delivery_options:\n      :redis_url: redis://localhost:6379\n      :worker: EmailReceiverWorker\n  -\n    :email: \"user8@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: postback\n    :delivery_options:\n      :delivery_url: \"http://localhost:3000/inbox\"\n      :jwt_auth_header: \"Mailroom-Api-Request\"\n      :jwt_issuer: \"mailroom\"\n      :jwt_algorithm: \"HS256\"\n      :jwt_secret_path: \"/etc/secrets/mailroom/.mailroom_secret\"\n```\n\n**Note:** If using `delete_after_delivery`, you also probably want to use\n`expunge_deleted` unless you really know what you're doing.\n\n## inbox_method\n\nBy default, IMAP mode is assumed for reading a mailbox.\n\n### IMAP Server Configuration ##\n\nYou can set per-mailbox configuration for the IMAP server's `host` (default: 'imap.gmail.com'), `port` (default: 993), `ssl` (default: true), and `start_tls` (default: false).\n\nIf you want to set additional options for IMAP SSL you can pass a YAML hash to match [SSLContext#set_params](http://docs.ruby-lang.org/en/2.2.0/OpenSSL/SSL/SSLContext.html#method-i-set_params). If you set `verify_mode` to `:none` it'll replace with the appropriate constant.\n\nIf you're seeing the error `Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)`, you need to configure your Gmail account to allow less secure apps to access it: https://support.google.com/accounts/answer/6010255.\n\n### Microsoft Graph configuration\n\nTo use the Microsoft Graph API instead of IMAP to read e-mail, you will\nneed to create an application in Microsoft Entra ID (formerly known as Azure Active Directory). See the\n[Microsoft instructions](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) for more details:\n\n1. Sign in to the [Azure portal](https://portal.azure.com).\n1. Search for and select `Microsoft Entra ID`.\n1. Under `Manage`, select `App registrations` \u003e `New registration`.\n1. Enter a `Name` for your application, such as `MailRoom`. Users of your app might see this name, and you can change it later.\n1. If `Supported account types` is listed, select the appropriate option.\n1. Leave `Redirect URI` blank. This is not needed.\n1. Select `Register`.\n1. Under `Manage`, select `Certificates \u0026 secrets`.\n1. Under `Client secrets`, select `New client secret`, and enter a name.\n1. Under `Expires`, select `Never`, unless you plan on updating the credentials every time it expires.\n1. Select `Add`. Record the secret value in a safe location for use in a later step.\n1. Under `Manage`, select `API Permissions` \u003e `Add a permission`. Select `Microsoft Graph`.\n1. Select `Application permissions`.\n1. Under the `Mail` node, select `Mail.ReadWrite`, and then select Add permissions.\n1. If `User.Read` is listed in the permission list, you can delete this.\n1. Click `Grant admin consent` for these permissions.\n\n#### Restrict mailbox access\n\nNote that for MailRoom to work as a service account, this application\nmust have the `Mail.ReadWrite` to read/write mail in *all*\nmailboxes. However, while this appears to be security risk,\nwe can configure an application access policy to limit the\nmailbox access for this account. [Follow these instructions](https://docs.microsoft.com/en-us/graph/auth-limit-mailbox-access)\nto setup PowerShell and configure this policy.\n\n#### MailRoom config for Microsoft Graph\n\nIn the MailRoom configuration, set `inbox_method` to `microsoft_graph`.\nYou will also need:\n\n* The client and tenant ID from the `Overview` section in the Azure app page\n* The client secret created earlier\n\nFill in `inbox_options` with these values:\n\n```yaml\n    :inbox_method: microsoft_graph\n    :inbox_options:\n      :tenant_id: 12345\n      :client_id: ABCDE\n      :client_secret: YOUR-SECRET-HERE\n      :poll_interval: 60\n```\n\nBy default, MailRoom will poll for new messages every 60 seconds. `poll_interval` configures the number of\nseconds to poll. Setting the value to 0 or under will default to 60 seconds.\n\n### Alternative Azure cloud deployments\n\nMailRoom will default to using the standard Azure HTTPS endpoints. To\nconfigure MailRoom with Microsoft Cloud for US Government or other\n[national cloud deployments](https://docs.microsoft.com/en-us/graph/deployments), set\nthe `azure_ad_endpoint` and `graph_endpoint` accordingly. For example,\nfor Microsoft Cloud for US Government:\n\n```yaml\n    :inbox_method: microsoft_graph\n    :inbox_options:\n      :tenant_id: 12345\n      :client_id: ABCDE\n      :client_secret: YOUR-SECRET-HERE\n      :poll_interval: 60\n      :azure_ad_endpoint: https://login.microsoftonline.us\n      :graph_endpoint: https://graph.microsoft.us\n```\n\n## delivery_method ##\n\n### postback ###\n\nRequires `faraday` gem be installed.\n\n*NOTE:* If you're using Ruby `\u003e= 2.0`, you'll need to use Faraday from `\u003e= 0.8.9`. Versions before this seem to have some weird behavior with `mail_room`.\n\nThe default delivery method, requires `delivery_url` and `delivery_token` in\nconfiguration.\n\nYou can pass `content_type:` option to overwrite `faraday's` default content-type(`application/x-www-form-urlencoded`) for post requests, we recommend passing `text/plain` as content-type.\n\nAs the postback is essentially using your app as if it were an API endpoint,\nyou may need to disable forgery protection as you would with a JSON API.\n\n### sidekiq ###\n\nDeliver the message by pushing it onto the configured Sidekiq queue to be handled by a custom worker.\n\nRequires `redis` gem to be installed.\n\nConfigured with `:delivery_method: sidekiq`.\n\nDelivery options:\n- **redis_url**: The Redis server to connect with. Use the same Redis URL that's used to configure Sidekiq.\n  Required, defaults to `redis://localhost:6379`.\n- **sentinels**: A list of sentinels servers used to provide HA to Redis. (see [Sentinel Support](#sentinel-support))\n  Optional.\n- **namespace**: [DEPRECATED] The Redis namespace Sidekiq works under. Use the same Redis namespace that's used to configure Sidekiq.\n  Optional.\n- **queue**: The Sidekiq queue the job is pushed onto. Make sure Sidekiq actually reads off this queue.\n  Required, defaults to `default`.\n- **worker**: The worker class that will handle the message.\n  Required.\n\nAn example worker implementation looks like this:\n\n\n```ruby\nclass EmailReceiverWorker\n  include Sidekiq::Worker\n\n  def perform(message)\n    mail = Mail::Message.new(message)\n\n    puts \"New mail from #{mail.from.first}: #{mail.subject}\"\n  end\nend\n```\n\n### que ###\n\nDeliver the message by pushing it onto the configured Que queue to be handled by a custom worker.\n\nRequires `pg` gem to be installed.\n\nConfigured with `:delivery_method: que`.\n\nDelivery options:\n- **host**: The postgresql server host to connect with. Use the database you use with Que.\n  Required, defaults to `localhost`.\n- **port**: The postgresql server port to connect with. Use the database you use with Que.\n  Required, defaults to `5432`.\n- **database**: The postgresql database to use. Use the database you use with Que.\n  Required.\n- **queue**: The Que queue the job is pushed onto. Make sure Que actually reads off this queue.\n  Required, defaults to `default`.\n- **job_class**: The worker class that will handle the message.\n  Required.\n- **priority**: The priority you want this job to run at.\n  Required, defaults to `100`, lowest Que default priority.\n\nAn example worker implementation looks like this:\n\n```ruby\nclass EmailReceiverJob \u003c Que::Job\n  def run(message)\n    mail = Mail::Message.new(message)\n\n    puts \"New mail from #{mail.from.first}: #{mail.subject}\"\n  end\nend\n```\n\n### logger ###\n\nConfigured with `:delivery_method: logger`.\n\nIf the `:log_path:` delivery option is not provided, defaults to `STDOUT`\n\n### noop ###\n\nConfigured with `:delivery_method: noop`.\n\nDoes nothing, like it says.\n\n### letter_opener ###\n\nRequires `letter_opener` gem be installed.\n\nConfigured with `:delivery_method: letter_opener`.\n\nUses Ryan Bates' excellent [letter_opener](https://github.com/ryanb/letter_opener) gem.\n\n## ActionMailbox in Rails ##\n\nMailRoom can deliver mail to Rails using the ActionMailbox [configuration options for an SMTP relay](https://edgeguides.rubyonrails.org/action_mailbox_basics.html#configuration).\n\nIn summary (from the ActionMailbox docs)\n\n1. Configure Rails to use the `:relay` ingress option:\n```rb\n# config/environments/production.rb\nconfig.action_mailbox.ingress = :relay\n```\n\n2. Generate a strong password (e.g., using SecureRandom or something) and add it to Rails config:\nusing `rails credentials:edit` under `action_mailbox.ingress_password`.\n\nAnd finally, configure MailRoom to use the postback configuration with the options:\n\n```yaml\n:delivery_method: postback\n:delivery_options:\n  :delivery_url: https://example.com/rails/action_mailbox/relay/inbound_emails\n  :username: actionmailbox\n  :password: \u003cINGRESS_PASSWORD\u003e\n```\n\n## Receiving `postback` in Rails ##\n\nIf you have a controller that you're sending to, with forgery protection\ndisabled, you can get the raw string of the email using `request.body.read`.\n\nI would recommend having the `mail` gem bundled and parse the email using\n`Mail.read_from_string(request.body.read)`.\n\n*Note:* If you get the exception (`Rack::QueryParser::InvalidParameterError (invalid %-encoding...`)\nit's probably because the content-type is set to Faraday's default, which is  `HEADERS['content-type'] = 'application/x-www-form-urlencoded'`. It can cause `Rack` to crash due to `InvalidParameterError` exception. When you send a post with `application/x-www-form-urlencoded`, `Rack` will attempt to parse the input and can end up raising an exception, for example if the email that you are forwarding contain `%%` in its content or headers it will cause Rack to crash with the message above.\n\n## idle_timeout ##\n\nBy default, the IDLE command will wait for 29 minutes (in order to keep the server connection happy).\nIf you'd prefer not to wait that long, you can pass `idle_timeout` in seconds for your mailbox configuration.\n\n## Search Command ##\n\nThis setting allows configuration of the IMAP search command sent to the server. This still defaults 'UNSEEN'. You may find that 'NEW' works better for you.\n\n## Running in Production ##\n\nI suggest running with either upstart or init.d. Check out this wiki page for some example scripts for both: https://github.com/tpitale/mail_room/wiki/Init-Scripts-for-Running-mail_room\n\n## Arbitration ##\n\nWhen running multiple instances of MailRoom against a single mailbox, to try to prevent delivery of the same message multiple times, we can configure Arbitration using Redis.\n\n```yaml\n:mailboxes:\n  -\n    :email: \"user1@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: postback\n    :delivery_options:\n      :delivery_url: \"http://localhost:3000/inbox\"\n      :delivery_token: \"abcdefg\"\n\n    :arbitration_method: redis\n    :arbitration_options:\n      # The Redis server to connect with. Defaults to redis://localhost:6379.\n      :redis_url: redis://redis.example.com:6379\n      # [DEPRECATED] The Redis namespace to house the Redis keys under. Optional.\n      :namespace: mail_room\n  -\n    :email: \"user2@gmail.com\"\n    :password: \"password\"\n    :name: \"inbox\"\n    :delivery_method: postback\n    :delivery_options:\n      :delivery_url: \"http://localhost:3000/inbox\"\n      :delivery_token: \"abcdefg\"\n\n    :arbitration_method: redis\n    :arbitration_options:\n      # When pointing to sentinel, follow this sintax for redis URLs:\n      # redis://:\u003cpassword\u003e@\u003cmaster-name\u003e/\n      :redis_url: redis://:password@my-redis-sentinel/\n      :sentinels:\n        -\n          :host: 127.0.0.1\n          :port: 26379\n      # [DEPRECATED] The Redis namespace to house the Redis keys under. Optional.\n      :namespace: mail_room\n```\n\n**Note:** This will likely never be a _perfect_ system for preventing multiple deliveries of the same message, so I would advise checking the unique `message_id` if you are running in this situation.\n\n**Note:** There are other scenarios for preventing duplication of messages at scale that _may_ be more appropriate in your particular setup. One such example is using multiple inboxes in reply-by-email situations. Another is to use labels and configure a different `SEARCH` command for each instance of MailRoom.\n\n## Sentinel Support\n\nRedis Sentinel provides high availability for Redis. Please read their [documentation](http://redis.io/topics/sentinel)\nfirst, before enabling it with mail_room.\n\nTo connect to a Sentinel, you need to setup authentication to both sentinels and redis daemons first, and make sure\nboth are binding to a reachable IP address.\n\nIn mail_room, when you are connecting to a Sentinel, you have to inform the `master-name` and the `password` through\n`redis_url` param, following this syntax:\n\n```\nredis://:\u003cpassword\u003e@\u003cmaster-name\u003e/\n```\n\nYou also have to inform at least one pair of `host` and `port` for a sentinel in your cluster.\nTo have a minimum reliable setup, you need at least `3` sentinel nodes and `3` redis servers (1 master, 2 slaves).\n\n## Logging ##\n\nMailRoom will output JSON-formatted logs to give some observability into its operations.\n\nSimply configure a `log_path` for the `logger` on any of your mailboxes. By default, nothing will be logged.\n\nIf you wish to log to `STDOUT` or `STDERR` instead of a file, you can pass `:stdout` or `:stderr`,\nrespectively and MailRoom will log there.\n\n## Contributing ##\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n6. If accepted, ask for commit rights\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpitale%2Fmail_room","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftpitale%2Fmail_room","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpitale%2Fmail_room/lists"}