Simple .NET application converting "e-mails" to REST API calls. Can be executed as a Docker container, as a Windows Service or self-hosted using the standard .NET host builder pattern.
I created this application because I needed to trigger some REST API services in my home automation system when certain types of motion events happened on my CCTV cameras. Although my cameras are ONVIF compliant, they do no not expose these motion events via the ONVIF Profile S, and thus are unavailable in my home automation system integration.
My workaround for this problem was to configure the cameras to "send an e-mail" when the desired motion events occurred, but instead of using my normal SMTP server to send the email, I configured the cameras to use the SMTP service hosted by the code in this repository instead. This service then converts those "e-mails" into the desired REST API calls I need in my home automation system.
The project has since then become a hobby project where I am experimenting with adding Docker support, CI/CD pipelines and other fun stuff.
The sample configuration below illustrates the current possibilites.
{
"smtpHost": "localhost",
"smtpPorts": [ 25, 587 ],
"apiToken": "<place your API token here if needed>",
"endpoint": "https://jsonplaceholder.typicode.com/",
"httpMethod": "GET",
"smtpRelay": {
"enabled": true,
"host": "smtp.gmail.com",
"port": 587,
"authenticate": true,
"username": "myprimaryuser@gmail.com",
"password": "mypassword"
},
"mappings": [
{
"key": "list.posts@somedomain.com",
"service": "posts",
"smtpRelay": {
"enabled": false
}
},
{
"key": "add.post@somedomain.com",
"customHttpMethod": "POST",
"service": "posts",
"content": {
"title": "Test post",
"body": "Test post body sent from $(from)",
"userId": 1
}
},
{
"key": "add.get@somedomain.com",
"customApiToken": "eyJ0eXAiOiJKV1QLKiFhbGciOiJIUzI1NiJ9.eyJpc3MieJoLYjY0ZTZkMThh...<cutoff>",
"customEndpoint": "https://somerestapi.com/",
"service": "posts",
"queryString": "title=Test+post&body=Test+post+body&userId=1",
"smtpRelay": {
"enabled": true,
"host": "192.168.1.100",
"port": 25,
"authenticate": false
}
}
]
}
Property | Description |
---|---|
smtpHost | Optional Defines the host (endpoint) where the SMTP server will be listening. Defaults to "localhost". Note Does not support runtime updates |
smtpPorts | Optional Defines ports the SMTP server will be listening on. Defaults to ports 25 and 587. Note Does not support runtime updates |
apiToken | Optional Defines the API token used for mappings (unless overridden in the mapping). Should be set if the REST service requires you to provide an API key. |
endpoint | Required - if "customEndpoint" not set on mapping Defines the common endpoint used for mappings (unless overridden in the mapping). |
httpMethod | Optional - defaults to "GET" Defines the common HTTP method to use for mappings (unless overridden in the mapping). |
mappings | Optional (but boring service if omitted) Defines a list of mappings (see below). |
smtpRelay | Optional Defines the shared configuration for SMTP relay used to send/relay the e-mail (see below). |
A mapping is what the services uses to convert an e-mail into a REST API call. If a mapping is found where the key of the mapping matches the to address of the e-mail, the REST API call defined by the mapping is invoked.
Property | Description |
---|---|
customHttpClientName | Optional HTTP Client name used for this particular mapping. Should be set if you have injected your own named IHttpClient into the DI container. |
customApiToken | Optional API token used for this particular mapping. Should be set if the REST service requires you to provide an API key. |
customEndpoint | Optional Defines the endpoint for this particular mapping. |
customHttpMethod | Optional Defines the HTTP method for this particular mapping. |
service | Required Defines the path appended to the enpoint to complete the URL. |
queryString | Optional Defines a query string to be appended to the URL (used in GET requests). |
content | Optional Defines the content (often times a JSON object) to be set as the content of the request (often used in POST requests). |
smtpRelay | Optional Defines the configuration for mapping-specific SMTP relay used to send/relay the e-mail (see below). |
The SMTP relay is used to forward the e-mail to another SMTP server. This is useful if you want to forward the e-mail to another service/recipient. All values are optional and will fall-back to the default from the configuration if omitted in the mapping.
Property | Description |
---|---|
host | Optional Defines the hostname or IP address of the SMTP server. |
port | Optional Defines the port used to connect to the SMTP server. |
authenticate | Optional Defines whether or not authentication should be used when connecting to the SMTP server. |
username | Optional Defines the username to use for authentication. |
password | Optional Defines the password to use for authentication. |
The following tokens can be used in the configuration and will be replaced with the corresponding values from the e-mail:
Token | Description |
---|---|
$(from) | The The (first) sender address |
$(to) | The (first) recipient address |
$(body) | The (string) body of the e-mail |
Each token can be applied as shown above to simply extract the token value in its entirety, or alternatively the token syntax below can be used to narrow down the selection.
Name | Syntax | Example | Description |
---|---|---|---|
Substring | $(<token>){<startIndex>} | $(body){15} | Will take the substring of the token starting at the provided <startIndex>. |
Substring and length | $(<token>){<startIndex>,<length>} | $(body){15,10} | Will take the substring of the token starting at the provided <startIndex> and <length> characters forward. |
Substring from index of string | $(<token>){[<stringToFind>]} | $(body){[hello]} | Will take the substring of the token starting at index of the provided <stringToFind>. |
Substring from index of string with offset | $(<token>){[<stringToFind>]+<offset>} | $(body){[hello]+10} | Will take the substring of the token starting at index of the provided <stringToFind> using the provided offset. Note the offset can also be negative. |
Substring from index of string and length | $(<token>){[<stringToFind>],<length>} | $(body){[hello],40} | Will take the substring of the token starting at index of the provided <stringToFind> and <length> characters forward. |
Substring from index of string to index of another string | $(<token>){[<stringToFind1>],[<stringToFind2>]} | $(body){[hello],[world]} | Will take the substring of the token starting at index of the provided <stringToFind1> up until the inded of the provided <stringToFind2>. |
Note that the list above is not exhaustive. It demonstrates the different elements of the syntax that can be applied, and these can be mixed and matched for the desirable "from" and "length"/"to" combination.
See SmtpToRest.Docker for more information.
See SmtpToRest.WindowsService for more information.
See SmtpToRest for more information.
In order to minimize my workload, I used the following Nuget packages:
Package | Author | Usage |
---|---|---|
SmtpServer | Cain O'Sullivan | I use this package to self-host an SMTP server. |
MailKit | Jeffrey Stedfast | I use this package to convert a byte-stream into a strongly typed MIME object and as an SMTP relay to forward e-mail messages. |