私は以前、自分のメールアドレスを使ってニュースレターを購読していましたが、毎日たくさんのメールが送られてきて、リマインダーや交流のメールと混ざってしまい、非常に面倒でした。
いくつかのニュースレター購読アプリを試したこともあり、mail2rss に関するウェブサイトやオープンソースプロジェクトについても理解しましたが、良い解決策は見つかりませんでした。
ある日、偶然に testmail.app という開発者がメールサービスをテストできるウェブサイトを見つけました。各ユーザーは 1 つの namespace を持ち、無限のメールアドレスを構築でき、メールをフィルタリングするための API もあります。
testmail の無料版では、毎月 100 通のメールを受信でき、メールは 1 日保存されます。ただし、RSS リーダーのリクエスト頻度を 1 日未満に設定すれば、すべてのメールを確実に受信できます。
そこで、プロジェクトのアイデアが浮かびました。serverless を使用して関数を実装し、この関数は RSS リーダーからのリクエストを受け取るたびに最新のメールリストを取得し、RSS 形式の XML ドキュメントを返すだけです。
プロジェクトの全体的なアイデアができたので、今はtestmail.app
の API に基づいて機能を作成するだけです。Serverless は Cloudflare Workers を使用することに決めました。毎日 10 万回のリクエストが可能で、I/O 時間は無制限です。
- testmail.app:https://testmail.app/
- Cloudflare Workers:https://workers.cloudflare.com/
testmail.app の使い方#
用語の説明#
登録してログインすると、自分の namespace が表示されます。
testmail.app では namespace について次のように説明しています:
testmail.app は、{namespace}.{tag}@inbox.testmail.app
に送信されたメールを受信します。{namespace}
は各ユーザーに割り当てられた独自の ID で、{tag}
は自分で自由に入力できます。その後、API を通じてタグをフィルタリングして必要なメールを取得できます。
namespace#
各 namespace は無限の電子メールアドレスをサポートしています。
例を挙げると、あなたの namespace が acmeinc だとします。次に、[email protected]
にメールを送信し、さらに[email protected]
に別のメールを送信すると、acmeinc という namespace の下でこの 2 通のメールを確認できます。ただし、2 通のメールが持つタグは異なります。
tag#
tag は任意の内容にできます。
例を挙げると、新しいユーザー登録機能をテストしたい場合、[email protected]
というメールアドレスを使用してユーザー名 John の新しいユーザーを作成し、[email protected]
というメールアドレスを使用してユーザー名 Albert の新しいユーザーを作成できます。
その後、API を照会する際に、指定したタグでフィルタリングして各ユーザーに送信されたメールを確認できます。
API の使用#
ドキュメントの URL:https://testmail.app/docs/
testmail.app は、直接パラメータを使用したクエリと GraphQL の 2 つのクエリ方法をサポートしています。どちらの方法も、メールリスト、各メールの内容、添付ファイル、メタ情報などを含む JSON 形式のレスポンスを直接取得します。
クエリを行う前に API キーを取得する必要があります。これはログイン後にウェブページ上で確認できます。API キーを headers のAuthorization
に入力すれば大丈夫です。
ここでは詳細には触れませんが、API を照会して必要なメールの内容を取得します。たとえば、quartz というタグを使用して QuartZ を購読する場合、そのタグのメールだけを照会すればよいのです。
js でメールをリクエストする実装:
const testmailNamespace = 'xxxxx';
const testmailToken = 'xxxxxxxxxxxxxxx';
class TestMail {
static testmailApi = 'https://api.testmail.app/api/graphql';
static async getMails(tag) {
const query = `{
inbox (
namespace: "${testmailNamespace}"
tag: "${tag}"
limit: 99
) {
emails {
id
subject
html
from
timestamp
downloadUrl
attachments {
cid
downloadUrl
}
}
}
}`;
const init = {
method: 'POST',
headers: {
'content-type': 'application/json;charset=UTF-8',
Authorization: `Bearer ${testmailToken}`,
Accept: 'application/json',
},
body: JSON.stringify({
operationName: null,
query,
variables: {},
}),
};
return fetch(this.testmailApi, init);
}
}
ここでは GraphQL のリクエスト方式を使用していることに注意してください。その後、await TestMail.getMails(tag)
を呼び出すだけで、対応するタグのメールを取得できます。
Cloudflare Workers の使い方#
私たちの考えに従って、Serverless 関数が中間層として機能し、RSS リーダーのリクエストに基づいて RSS コンテンツを生成する必要があります。
したがって、次のことが必要です:
- ユーザーリクエストのタグを取得
- タグに対応するメールリストをリクエスト
- XML 形式のウェブページを返す。
最初のステップは、ユーザーリクエストのアドレスを解析し、タグを取得することです。たとえば、ユーザーがmail2rss.test.workers.dev/tenjs
をリクエストした場合、tenjs
を抽出する必要があります。
Cloudflare Workers では、event.request.url
を使用してユーザーリクエストの完全な URL を取得できます。他の Serverless サービスを使用している場合、koa-like
であれば、一般的に Request を通じて URL を取得します。
const {request} = event;
let url = new URL(request.url);
// タグを解析
const requestTag = url.pathname.substring(1);
第二のステップとして、メールリストのリクエストはすでに書いてあります。
第三のステップは、XML 形式のレスポンスを生成して返すことです。
XML 生成の実装#
ここではテンプレートエンジンを使用せず、文字列を直接結合するだけです。
まず、RSS のファイル形式を知る必要があります。ここではRSSHub のファイル内容を参考にしました。そして、makeRss
関数を作成し、文字列を結合して生成します。
async function makeRss(emails, tag) {
let items = emails.map((value) => {
if (value.attachments.length > 0) {
for (let i of value.attachments) {
// 画像リンクを更新
value.html = value.html.replace(`cid:${i.cid}`, i.downloadUrl);
}
}
return `<item>
<title><![CDATA[${value.subject}]]></title>
<description><![CDATA[${value.html}]]></description>
<pubDate>${new Date(value.timestamp).toGMTString()}</pubDate>
<guid isPermaLink="false">${value.id}</guid>
<link>${value.downloadUrl}</link>
<author><![CDATA[${value.from}]]></author>
</item>`;
});
return `<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title><![CDATA[${tag}メール購読]]></title>
<link>${deployUrl + tag}</link>
<atom:link href="${
deployUrl + tag
}" rel="self" type="application/rss+xml" />
<description><![CDATA[${tag}メール購読]]></description>
<generator>mail2rss</generator>
<webMaster>[email protected] (Artin)</webMaster>
<language>ja</language>
<lastBuildDate>${new Date().toGMTString()}</lastBuildDate>
<ttl>300</ttl>
${items.join('\n')}
</channel>
</rss>`;
}
そして、レスポンスを返します:
let responseXML = await makeRss(data.data.inbox.emails, requestTag);
let response = new Response(responseXML, {
status: 200,
headers: {
'content-type': 'application/xml; charset=utf-8',
},
});
response.headers.append('Cache-Control', 'max-age=600');
return response;
まとめ#
実際、考え方は非常にシンプルで、コードも簡単です。
完全なコードは次のリンクで確認できます:https://github.com/bytemain/mail2rss
参考リンク#
- DIYgod/RSSHub
https://github.com/DIYgod/RSSHub - testmail.app
https://testmail.app/ - testmail.app Documentation
https://testmail.app/docs/ - Cloudflare Workers
https://workers.cloudflare.com/