worker.js を追加
This commit is contained in:
parent
f4fdbd33eb
commit
875f61b62d
1 changed files with 158 additions and 0 deletions
158
worker.js
Normal file
158
worker.js
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
// いじりやすいようにここに変数を集約させたい
|
||||||
|
const BLOCKLIST = ["hacker@example.com", "spammer@example.com"];
|
||||||
|
const FORWARD_TO = "trendcreate2021@gmail.com";
|
||||||
|
const WEBHOOK_URL = "https://discord.com/api/webhooks/xxx/xxx";
|
||||||
|
// trendcreate icon
|
||||||
|
const WEBHOOK_ICON = "https://git.v-sli.me/HidemaruOwO/email-worker/raw/branch/main/assets/webhook_icon.jpg";
|
||||||
|
// mail letter icon
|
||||||
|
const AUTHOR_ICON = "https://git.v-sli.me/HidemaruOwO/email-worker/raw/branch/main/assets/email_icon.png";
|
||||||
|
// cloudflare icon
|
||||||
|
const FOOTER_ICON = "https://git.v-sli.me/HidemaruOwO/email-worker/raw/branch/main/assets/cloudflare_icon.ico";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async email(message, env, ctx) {
|
||||||
|
if (BLOCKLIST.includes(message.from)) {
|
||||||
|
message.setReject("Address is blocked");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [text] = await Promise.all([
|
||||||
|
getMailText(message),
|
||||||
|
message.forward(FORWARD_TO)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await notify(message.headers.get("from"), message.headers.get('subject'), text, message.headers.get("date"));
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
console.log(await result.text())
|
||||||
|
console.log(await result.json())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("処理エラー:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function notify(from, subject, text, date) {
|
||||||
|
const payload = {
|
||||||
|
username: "contact@trendcreate.net",
|
||||||
|
avatar_url: WEBHOOK_ICON,
|
||||||
|
content: `**${from}**からお問い合わせメールが届いております。`,
|
||||||
|
embeds: [{
|
||||||
|
author: {name: from || "名前なし", icon_url: AUTHOR_ICON},
|
||||||
|
title: `**${subject || "件名なし"}**`,
|
||||||
|
description: text || "本文はありません。",
|
||||||
|
timestamp: new Date(date).toISOString(),
|
||||||
|
footer: {
|
||||||
|
text: 'Powered by Cloudflare Worker and Email Routing',
|
||||||
|
icon_url: FOOTER_ICON
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(payload))
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await fetch(WEBHOOK_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
throw new Error(err)
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMailText(message) {
|
||||||
|
try {
|
||||||
|
const buf = await readStream(message.raw);
|
||||||
|
if (buf === "NO_CONTENT") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return parseEmail(buf);
|
||||||
|
} catch (err) {
|
||||||
|
return `エラー: ${err.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readStream(stream) {
|
||||||
|
if (typeof stream === "undefined") {
|
||||||
|
return "NO_CONTENT"
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunks = [];
|
||||||
|
const reader = stream.getReader();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
chunks.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
||||||
|
const result = new Uint8Array(size);
|
||||||
|
|
||||||
|
let pos = 0;
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
result.set(chunk, pos);
|
||||||
|
pos += chunk.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEmail(buffer) {
|
||||||
|
const text = new TextDecoder().decode(buffer);
|
||||||
|
const headerEnd = text.indexOf('\r\n\r\n');
|
||||||
|
|
||||||
|
if (headerEnd === -1) return '';
|
||||||
|
|
||||||
|
const header = text.substring(0, headerEnd);
|
||||||
|
const body = text.substring(headerEnd + 4);
|
||||||
|
|
||||||
|
const boundaryMatch = header.match(/boundary="?([^"\r\n]+)"?/i);
|
||||||
|
|
||||||
|
if (!boundaryMatch) return body.trim();
|
||||||
|
|
||||||
|
const boundary = `--${boundaryMatch[1]}`;
|
||||||
|
const parts = body.split(boundary);
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (part.includes('Content-Type: text/plain')) {
|
||||||
|
const isBase64 = part.includes('Content-Transfer-Encoding: base64');
|
||||||
|
const partBody = part.split('\r\n\r\n')[1]?.trim();
|
||||||
|
|
||||||
|
if (!partBody) continue;
|
||||||
|
|
||||||
|
if (isBase64) {
|
||||||
|
try {
|
||||||
|
const cleanBase64 = partBody.replace(/[\r\n\s]/g, '');
|
||||||
|
return decodeBase64(cleanBase64);
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return partBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeBase64(base64) {
|
||||||
|
const binary = atob(base64);
|
||||||
|
const bytes = new Uint8Array(binary.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
bytes[i] = binary.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextDecoder().decode(bytes);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue