野声

Hey, 野声!

谁有天大力气可以拎着自己飞呀
twitter
github

無料のニュースレターをRSSに変換するソリューション

私は以前、自分のメールアドレスを使ってニュースレターを購読していましたが、毎日たくさんのメールが送られてきて、リマインダーや交流のメールと混ざってしまい、非常に面倒でした。

いくつかのニュースレター購読アプリを試したこともあり、mail2rss に関するウェブサイトやオープンソースプロジェクトについても理解しましたが、良い解決策は見つかりませんでした。

ある日、偶然に testmail.app という開発者がメールサービスをテストできるウェブサイトを見つけました。各ユーザーは 1 つの namespace を持ち、無限のメールアドレスを構築でき、メールをフィルタリングするための API もあります。

testmail の無料版では、毎月 100 通のメールを受信でき、メールは 1 日保存されます。ただし、RSS リーダーのリクエスト頻度を 1 日未満に設定すれば、すべてのメールを確実に受信できます。

そこで、プロジェクトのアイデアが浮かびました。serverless を使用して関数を実装し、この関数は RSS リーダーからのリクエストを受け取るたびに最新のメールリストを取得し、RSS 形式の XML ドキュメントを返すだけです。

Mermaid Loading...

プロジェクトの全体的なアイデアができたので、今はtestmail.appの API に基づいて機能を作成するだけです。Serverless は Cloudflare Workers を使用することに決めました。毎日 10 万回のリクエストが可能で、I/O 時間は無制限です。

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 を照会する際に、指定したタグでフィルタリングして各ユーザーに送信されたメールを確認できます。

namespace

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 コンテンツを生成する必要があります。

したがって、次のことが必要です:

  1. ユーザーリクエストのタグを取得
  2. タグに対応するメールリストをリクエスト
  3. 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

参考リンク#

  1. DIYgod/RSSHub
    https://github.com/DIYgod/RSSHub
  2. testmail.app
    https://testmail.app/
  3. testmail.app Documentation
    https://testmail.app/docs/
  4. Cloudflare Workers
    https://workers.cloudflare.com/
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。