{"id":20917940,"url":"https://github.com/avigoldman/fuse-email","last_synced_at":"2025-05-13T12:31:05.848Z","repository":{"id":57243276,"uuid":"74985769","full_name":"avigoldman/fuse-email","owner":"avigoldman","description":" A framework for writing conversational email responders","archived":false,"fork":false,"pushed_at":"2017-08-17T20:39:37.000Z","size":71,"stargazers_count":9,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-11T02:02:26.535Z","etag":null,"topics":["conversation","fuse","receive-emails","receive-messages","responder","sparkpost"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/avigoldman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-11-28T15:21:04.000Z","updated_at":"2021-08-12T10:02:12.000Z","dependencies_parsed_at":"2022-09-15T09:02:44.144Z","dependency_job_id":null,"html_url":"https://github.com/avigoldman/fuse-email","commit_stats":null,"previous_names":["avrahamgoldman/fuse-email"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avigoldman%2Ffuse-email","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avigoldman%2Ffuse-email/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avigoldman%2Ffuse-email/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avigoldman%2Ffuse-email/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/avigoldman","download_url":"https://codeload.github.com/avigoldman/fuse-email/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225023785,"owners_count":17408774,"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":["conversation","fuse","receive-emails","receive-messages","responder","sparkpost"],"created_at":"2024-11-18T16:36:41.188Z","updated_at":"2024-11-18T16:36:41.888Z","avatar_url":"https://github.com/avigoldman.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/avrahamgoldman/fuse-email.svg?branch=master)](https://travis-ci.org/avrahamgoldman/fuse-email)\n[![Coverage Status](https://coveralls.io/repos/github/avrahamgoldman/fuse-email/badge.svg)](https://coveralls.io/github/avrahamgoldman/fuse-email)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n# Fuse - automating email conversations\n### A framework for writing conversational email responders\n\n[SparkPost](https://sparkpost.com) is a cloud email service that allows developers to send and receive emails.\n\nThis is a botkit-inspired framework written around the SparkPost API to make it easy to automate email conversations.\n\n##### Table of Contents\n[Getting Started](#getting-started)\u003cbr\u003e\n[Initialization](#initialization)\u003cbr\u003e\n[Start Listening](#start-listening)\u003cbr\u003e\n[The Address Object](#the-address-object)\u003cbr\u003e\n[Receiving Messages](#receiving-messages)\u003cbr\u003e\n[Sending Messages](#sending-messages)\u003cbr\u003e\n[Having Conversations](#having-conversations)\u003cbr\u003e\n[Helper Functions](#helper-functions)\u003cbr\u003e\n\n\n## Getting Started\n\nBefore setting this framework up you'll first need a SparkPost account. SparkPost lets you send 15,000 emails per month for free with their [Developer Account](https://www.sparkpost.com/pricing/#developer-plan) which should be plenty to get started. You can sign up [here](https://sparkpo.st/1lent).\n\n### Creating an API Key\n\nFor this framework to work, you will need to create an API Key with the following access:\n\nSending Domains: Read/Write\u003cbr\u003e\nInbound Domains: Read/Write\u003cbr\u003e\nRelay Webhooks: Read/Write\u003cbr\u003e\nTransmissions: Read/Write\u003cbr\u003e\n\nTo create the key visit the [API Keys](https://app.sparkpost.com/account/credentials) section in the SparkPost app under Account. \nOnce there selected the required permissions and store the generated key somewhere safe as you won't be able to access it again.\n\n### Configuring your sending domain\nThe sending domain is the domain that you will send emails from (.e.g. hi.there@sendingdomain.com).\n\nAdd your domain in the [Sending Domains](https://app.sparkpost.com/account/sending-domains) section. You'll need to verify ownership of the domain through the methods provided in the UI. \n\n### Configuring your inbound domain\n\nYour inbound domain is the domain at which you will receive emails. It can be the same address as your sending domain.\n\nAdd the following MX records to your inbound domain's DNS\n\nName | Type | Data | Priority\n---- | ---- | ---- | --------\n`your.inbounddomain.com` | MX | rx1.sparkpostmail.com | 10\n`your.inbounddomain.com` | MX | rx2.sparkpostmail.com | 10\n`your.inbounddomain.com` | MX | rx3.sparkpostmail.com | 10\n\n## Setting up your fuse\nOnce the DNS changes have propagated (you can check the [mxtoolbox](https://mxtoolbox.com/)), its time to build your responder.\n\n### Installation\n```\nnpm install fuse-email\n```\n\n## Initialization\n\n#### Fuse(config)\n* `config.email_key`\n  * Required: yes\n  * Type: `String`\n  * An API Key\n* `config.sending_address`\n  * Required: yes\n  * Type: `String`\n  * A valid email for the responder to send mail from\n* `config.inbound_address`\n  * Required: yes\n  * Type: `String`\n  * A valid email for the responder to receive mail at\n* `config.domain`\n  * Required: yes\n  * Type: `String`\n  * The domain you are using to host your responder\n* `config.endpoint`\n  * Required: no\n  * Type: `String`\n  * Default: `/relay`\n  * The path to send the relay webhook to  \n* `config.name`\n  * Required: no\n  * Type: `String`\n  * Default: `Sparky`\n  * The name of your responder\n* `config.auth_token`\n  * Required: no\n  * Type: `String`\n  * An Authentication Token for the relay webhook to use to verify its identity\n* `config.size_limit`\n  * Required: no\n  * Type: `String`\n  * Default: `50mb`\n  * The maximum post size - if you get `request entity too large` errors increase this. \n* `config.restrict_inbound`\n  * Required: no\n  * Type: `Boolean`\n  * Default: `true`\n  * Whether or not to process inbound emails that were sent to an different email address\n* `config.transport`\n  * Required: no\n  * Type: `String`\n  * Default: `sparkpost`\n  * The transport to use\n\nNext you'll need to create a Fuse instance \n```\nvar Fuse = require('fuse-email');\n\nvar fuse = Fuse({\n  email_key: 'SPARKPOST_API_KEY',\n  name: 'NAME',\n  sending_address: 'robot@MY_SENDING_DOMAIN',\n  inbound_address: 'robot@MY_INBOUND_DOMAIN',\n  domain: 'MY_DOMAIN'\n});\n```\n\n\n## Start Listening\n\n#### `fuse.setupTransport(callback)`\nRuns the setup for the transport. This will make sure you are setup correctly with your transport. Do not use this in production.\n```\nfuse.setupTransport(function() {\n  // setup the server and endpoint\n});\n```\n\n\n#### `fuse.setupServer(port, callback)`\nStarts an express server at the given port. The callback is called  when the server is running.\n```\nfuse.setupServer(3000, function(err, server) {\n  // the server is up\n});\n```\n\n#### `fuse.setupEndpoint(server, callback)`\nSets up an endpoint based on `config.endpoint` to receive the data from the relay webhook from SparkPost. \n```\nfuse.setupServer(3000, function(err, server) {\n  fuse.setupEndpoint(server, function() {\n  \t// fuse is now running\n  });\n});\n```\n\n## The Address Object\nAll email addresses are in the following format\n```\n{\n  email: 'email@example.com',\n  name: 'My Name'\n}\n```\n\n## Receiving Messages\n\n### The events\nName | Description\n---- | -----------\n`direct_email` | The responder received an email as an original recipient\n`cc_email` | The responder received an email as a cc'd recipient\n`bcc_email` | The responder received an email as a bcc'd recipient\n`email_received` | The responder received an email - this always fires unless there is a conversation happening\n\n### The `inboundMessage` object\nThe `inboundMessage` object is returned to the event listeners.\n\nName | Type | Description\n---- | ---- | -----------\n`inboundMessage.id` | `String` | The message id of the `inboundMessage`\n`inboundMessage.event` | `String` | The event that triggered the callback\n`inboundMessage.to` | `Object` | The address object that received this email. *Unless `restrict_inbound` is set to `false`, the `to.email` will always be your `inbound_address`*\n`inboundMessage.from` | `Object` | The address object who sent this email\n`inboundMessage.subject` | `String` | The email subject\n`inboundMessage.text` | `String` | The plaintext email body\n`inboundMessage.html` | `String` | The html email body\n`inboundMessage.recipients` | `Array[Object]` | An array of the address objects of the original recipients\n`inboundMessage.cc` | `Array[Object]` | An array of the address objects of the cc'd recipients\n`inboundMessage.bcc` | `Array[Object]` | An array of the address objects of the bcc'd recipients\n`inboundMessage.headers` | `Object` | An object of the headers from the inbound email in a `{key: value}` format where `value` is a string if there is one value and an Array if two or more.\n`inboundMessage.attachments` | `Array` | An array of attachments from the email\n`inboundMessage._raw` | `*` | The original data received. For a SparkPost example look at the `relay_message` value [here](https://developers.sparkpost.com/api/relay-webhooks.html#header-example-payloads).\n\n\n##### Attachments example\n```\nattachments = [{\n    contentType: 'image/png',\n    fileName: 'image.png',\n    contentDisposition: 'attachment',\n    contentId: '5.1321281380971@localhost',\n    transferEncoding: 'base64',\n    length: 126,\n    generatedFileName: 'image.png',\n    checksum: 'e4cef4c6e26037bcf8166905207ea09b',\n    content: \u003cBuffer ...\u003e\n}];\n```\n\n### Registering Event Listeners\nThere are two methods for receiving messages: `on` and `hear`. Both of these functions take a callback. These callbacks are given a `responder` object and a `inboundMessage` object.\n\n#### `fuse.on(events, callback)`\nTo stop all subsequent listeners, return `false` \n\nName | Type | Description\n---- | ---- | -----------\n`events` | `String` or `Array` | A comma delaminated list or an array of events on which to run this function\n`callback` | `Function` | The function to be called when any of the given events take place\n\nExample:\n```\nfuse.on('email_received', function(responder, inboundMessage) {\n  responder.send({\n    subject: 'Hello World',\n    body: 'What a nice day we are having!'\n  });\n});\n```\n\n\n#### `fuse.hears(patterns, events, callback)`\n*If a message matches a `hears` listener all subsequent listeners are ignored.*\n\nThe `hears` function works just like the `on` function except it takes an extra parameter, `patterns`. This parameter can be either an array or object. If it's an array then the patterns are checked against the subject and body. If it's an object then it can have a `subject` and/or `body` property, each of which should be an array of patterns to check against. All the patterns should either be Regular Expressions or strings.\n\nExample:\n```\nfuse.hears({\n  subject: ['hello', 'hi'],\n  body: ['howdy', 'sup'],\n}, 'direct_email', function(responder, inboundMessage) {\n  responder.send({\n    subject: 'Hello there',\n    body: 'Hello to you too!'\n  });\n});\n```\n\n## Sending Messages\n\n### The `responder` object\nThe `responder` object drives responding to messages. It is returned to all event listeners.\nYou can create a standalone responder to send something or start a conversation by calling the `responder` method.\n\n```\nvar responder = fuse.responder();\n```\nKeep in mind that the `reply` and `startPrivateConversation` methods will not work as they are reactions to inbound messages.\n\n### The `outboundMessage` object\nName | Type | Description\n---- | ---- | -----------\n`message.subject` | `String` | The subject of the email.\n`message.body` or `message.html` | `String` | The body of the email.\n`message.text` | `String` | If this is not given then it will be generated from the html.\n`message.headers` | `Object` | Email headers other than “Subject”, “From”, “To”, and “Reply-To”\n`message.recipients` | `Array[Object]` | An array of email addresses.\n`message.cc` | `Array[Object]` |  An array of email addresses to receive a carbon copy.\n`message.bcc` | `Array[Object]` |  An array of email addresses to receive a blind carbon copy.\n`message.substitution_data` | `String` | Any substitution data for the email.\n`message.attachments` | `Array` | An array of attachments to send with the email. See the [SparkPost docs](https://developers.sparkpost.com/api/transmissions.html#header-attachment-attributes) for more details.\n`message.from` | `Object` | An address object to override `config.name` and `config.sending_address` for this message.\n`message.reply_to` | `Object` | An address object to override `config.name` and `config.inbound_address` for this message.\n\n#### `responder.send(outboundMessage)`\nThis will send send a new email with the given content. If recipients of any type are given the recipients and cc will default to the values from the received email.\n\n```\nfuse.on('direct_email', function(responder, inboundMessage) {\n  responder.send({\n    subject: 'Hello World',\n    body: '\u003ch2\u003eWhat a nice {{time}} we are having!\u003c/h2\u003e',\n    substitution_data: {\n    \ttime: 'day'\n    }\n  });\n});\n```\n\n#### `responder.reply(outboundMessage)`\n\nThis method is identical to the `send` method except it will reply to the sent message. As such you can not set the `subject`, `recipients`, or `cc`.\n\n```\nfuse.on('email_received', function(responder, inboundMessage) {\n  responder.reply({\n    body: 'I got your message!'\n  });\n});\n```\n\n\n## Having Conversations\n\nThe responder has two methods for starting conversations.\n\n#### `responder.startConversation(config, callback)`\nStarts a conversation with everyone from the received message or the specified `recipients` and `cc` recipients.\n\nName | Type | Required | Description\n---- | ---- | -------- | -----------\n`config` | `String` or `Object` | yes | If this is a string then it is the subject of the conversation.\n`config.subject` | `String` | yes | The subject for the new thread of messages for this conversation. This is required.\n`config.recipients` | `Array[Object]` | no |  An array of address objects. This will default to the recipients of the `inboundMessage` that was returned with this responder. This is required if starting a conversation from a standalone responder.\n`config.cc` | `Array[Object]` | no |  An array of address objects to receive a carbon copy. This will default to the cc'd recipients of the `inboundMessage` that was returned with this responder.\n`config.timeout_after` | `Number` | no | Milliseconds to wait before the conversation times out. Defaults to `600000`. (10 minutes).\n\n\n#### `responder.startPrivateConversation(topic, callback)`\nStarts a conversation with the person who sent the `inboundMessage`. This can not be used from a standalone responder.\n\n### the `convo` object\nWhen a conversation is started it the callback receives a convo object.\n\n#### `convo.send(outboundMessage)`\nThis works just like the `reply` method of the `responder` object. It will send the message to all the participants of this conversation.\n\n#### `convo.ask(outboundMessage, handler)`\nName | Type | Description\n---- | ---- | -----------\nmessage | `Object` | See the [outboundMessage options](#the-outboundmessage-object)\nhandler | `Function` | This will be called when someone replies to the question.\n\nThe handler function will receive a `message` object and the same `convo` object to continue the conversation.\n\n```\n// from event listener\nresponder.startConversation('Tell me about yourself!', function(convo) {\n  convo.ask({\n    body: 'What\\'s your name?'\n  }, function(convo, inboundMessage) {\n  \n    let name = sparky.clean(sparky.getLatest(inboundMessage));\n\n    convo.send({\n      body: 'Nice to meet you, {{name}}',\n      substitution_data: {\n        name: name\n      }\n    });\n\n  });\n\n}); \n```\n\n#### `convo.wait()`\nThis function keeps the conversation alive while preforming asynchronous tasks until you can ask a question. See an example [here](/tree/master/examples/async-wait.js).\n\n\n#### `convo.end()`\nCall this function to force the conversation to end.\n\n## Util functions\nThese are a few functions to make your life easier.\n\n#### `fuse.clean(str)`\nReturns the given string stripped of any html tags, trailing spaces, and line breaks.\n\n#### `fuse.getLatest(message)`\nReturns the latest text message from an email thread.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favigoldman%2Ffuse-email","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Favigoldman%2Ffuse-email","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favigoldman%2Ffuse-email/lists"}