') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - jpalala/java-ses: java based ses sending tool · GitHub
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

java-ses

Tiny AWS Lambda (Java 17) that sends e-mail via Amazon SES.

Configuration is done with Lambda environment variables; the same function can be triggered from the AWS console, CLI, SDK, or an API Gateway proxy.


1. Build

git clone <repo>cd java-ses
mvn clean package

Artifact: target/java-ses-1.0.0-all.jar


2. Create IAM role (once)

# trust policy – allow Lambda to assume the role
cat > trust.json <<EOF{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.amazonaws.com"}, "Action": "sts:AssumeRole" }]}EOF
aws iam create-role \
--role-name lambda-ses-email \
--assume-role-policy-document file://trust.json
# give Lambda basic logging rights
aws iam attach-role-policy \
--role-name lambda-ses-email \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# allow SES (least privilege – change to your verified address)
cat > ses-policy.json <<EOF{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["ses:SendEmail", "ses:SendRawEmail"], "Resource": "*", "Condition": { "StringEquals": {"ses:FromAddress": "verified@example.com"} } }]}EOF
aws iam put-role-policy \
--role-name lambda-ses-email \
--policy-name SES-Send-Policy \
--policy-document file://ses-policy.json

3. Deploy the function

aws lambda create-function \
--function-name send-email-java \
--runtime java17 \
--role arn:aws:iam::<ACCOUNT_ID>:role/lambda-ses-email \
--handler example.SendEmailHandler::handleRequest \
--zip-file fileb://target/java-ses-1.0.0-all.jar \
--timeout 30 \
--memory-size 512 \
--environment "Variables={\FROM_ADDRESS=verified@example.com,\TO_ADDRESS=destination@example.com,\AWS_REGION=us-east-1}"

Update variables later without redeploying code:

aws lambda update-function-configuration \
--function-name send-email-java \
--environment "Variables={\FROM_ADDRESS=new@example.com,\TO_ADDRESS=other@example.com,\AWS_REGION=us-east-1}"

4. Invoke

A. AWS CLI (synchronous)

aws lambda invoke \
--function-name send-email-java \
--payload '{ "subject": "CLI test", "bodyText": "Sent from AWS CLI", "bodyHtml": "<h1>CLI</h1><p>Sent from AWS CLI</p>" }' \
--cli-binary-format raw-in-base64-out \
response.json
cat response.json
# -> Email sent. MessageId=0100017f...

B. AWS CLI (asynchronous – fire-and-forget)

Add --invocation-type Event.

C. AWS SDK (Java example)

LambdaClientlambda = LambdaClient.create();
Stringpayload = """ { "subject": "SDK test", "bodyText": "Plain body", "bodyHtml": "<h1>HTML body</h1>" }""";
InvokeRequestreq = InvokeRequest.builder()
.functionName("send-email-java")
.payload(SdkBytes.fromUtf8String(payload))
.invocationType(InvocationType.REQUEST_RESPONSE)
.build();
InvokeResponseresp = lambda.invoke(req);
System.out.println(resp.payload().asUtf8String());

D. API Gateway (optional)

Create a new HTTP or REST API, add a POST /send method, integration type “Lambda”, and map the incoming JSON body straight through.
No extra code changes—Lambda already accepts the same JSON shown above.


5. Environment variables reference

NamePurposeExample
FROM_ADDRESSSES-verified sendernoreply@example.com
TO_ADDRESSDefault recipient (can be overridden in payload)admin@example.com
AWS_REGIONRegion for SES endpointus-east-1

All three are optional at build time; if you omit them you must supply them in the Lambda console or update-function-configuration.


6. Local testing (optional)

# unit test
mvn test# local invoke with AWS SAM
sam local invoke -e event.json
# event.json contains the same JSON payload used in the CLI example

7. Clean up

# 1. Delete the function first
aws lambda delete-function --function-name send-email-java
# 2. Delete the inline policy
aws iam delete-role-policy --role-name lambda-ses-email --policy-name SES-Send-Policy
# 3. Detach the managed execution policy
aws iam detach-role-policy --role-name lambda-ses-email --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# 4. Finally, delete the role
aws iam delete-role --role-name lambda-ses-email

Notes

API Gateway Integration (Important)

By default, this Lambda expects a clean, direct JSON payload containing the specific email keys.

  • If you use a REST API with a Custom (Non-Proxy) Integration: You must use a "Body Mapping Template" to pass the raw payload through. Read more about setting up payload transformations in the AWS Mapping Templates Guide.
  • If you use an HTTP API or a Lambda Proxy Integration: API Gateway will wrap your payload in a big metadata envelope before handing it to Lambda. To use this without mapping templates, you will need to update your Java code to receive an AWS Proxy Event and manually parse the body.

IAM Role Cleanup

When tearing down resources via the AWS CLI, programmatic rules apply. AWS will not let you delete an IAM role if it still has active dependencies. You must strip all its policies before you delete the role itself.

  1. Use delete-role-policy to remove inline policies.
  2. Use detach-role-policy to remove managed AWS policies.
  3. Finally, execute the delete-role command.

Troubleshooting

  • Make sure to read the above notes on IAM Role cleanup, as well as API Gateway Integration.
  • SES must be out of the sandbox in new accounts or the destination address verified.
  • If you get “Access denied” from SES, double-check the IAM policy’s FromAddress condition matches FROM_ADDRESS.
  • The jar must be < 250 MB unzipped and < 50 MB zipped for direct upload; this build is ~12 MB.
  • For heavier dependencies switch to container image or Lambda Layers.

About

java based ses sending tool

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages