รูปภาพสำหรับบทความเรื่อง "ไม่ชอบบันทึกรายรับรายจ่าย ทำยังไงจะอัตโนมัติ"

ไม่ชอบบันทึกรายรับรายจ่าย ทำยังไงจะอัตโนมัติ

การบันทึกรายรับรายจ่ายจริง ๆ เป็นสิ่งที่ควรทำ แต่ก็ไม่ขยันจะเพิ่มข้อมูล เลยนึกได้ว่ามันมีทางอยู่นี่หว่า

ผมมีแอปติดตามบันทึกรายรับรายจ่ายที่ชอบมากโดยส่วนตัว ถ้าใครกำลังหาแอปแนวนี้ผมก็จะแนะนำตัวนี้ตลอด ซึ่งคือ Cashew เป็นแอปที่เขียนโดยใช้ Material You สวยงามและฟีเจอร์ครบครันที่สุดเท่าที่ผมเคยใช้มา แถมยังฟรีและโอเพนซอร์ซอีก แต่บล็อกนี้ผมไม่ได้จะมาแนะนำแอปหรอก ตัวแอปมันดีของมันอยู่แล้ว ปัญหาคือผมขี้เกียจเพิ่มข้อมูลเองมากกว่า หลังจากเริ่มใช้แอปมาสักพักกลายเป็นว่ามีช่วงที่ขาดไปกว้างมากจนมีครั้งนึงต้องนั่งกดขอ Statement จากธนาคารมาเพิ่มเอาเอง 5555 (ซึ่งจริง ๆ ก็ไม่ได้แย่ แต่อ่าน pdf ให้เป้ะนี่ไม่ค่อนสนุกเท่าไหร่)

จากข้อมูล statement ที่เป็นไฟล์ PDF รีเควสจากในแอป ผมก็ได้พอทราบแล้วว่าธนาคารที่ให้รายละเอียดดีที่สุดคือกสิกรไทย ทั้งชื่อผู้รับและชื่อร้านค้า แต่ยังไม่ดีพอเพราะต้องมานั่งสกัดข้อมูลเองนี่แหละ AI ก็ช่วยได้เยอะแต่ถ้าพลาดคือหายากสุด ๆ

แต่แล้ววันหนึ่ง (เมื่ออาทิตย์ที่แล้ว) ก็นึกได้ว่าเราสามารถใช้ Cloudflare Email Routing + Email Worker เพื่อรับอีเมลจากธนาคาร แล้วสกัดข้อมูลไว้ทำเป็นรายรับรายจ่ายได้

วิธีการ

ก่อนที่จะทำอะไร โดเมนที่ใช้ต้องเป็นโดเมนที่ใช้ Cloudflare name server ก่อน โดยส่วนตัวแนะนำให้ใช้ Cloudfalre ถึงคุณจะซื้อโดเมนจากใครก็เถอะ

  1. สร้าง Worker ใหม่ที่เป็น email worker ซึ่งทำได้จากการใช้ cli หรือในแดชบอร์ดเลยก็ได้ แต่ในที่นี้ผมจะใช้ CLI จะได้จัดการ Source control ง่าย ๆ และใช้ Wrangler เริ่มต้นด้วยการ setup ตามเรื่องตามราว
pnpx wrangler login # ถ้ายังไม่ทำ
pnpm create cloudflare 
# หลังจากนั้นก็เลือกการตั้งค่าตามใจชอบ ในบล็อกนี้ใช้ TypeScript 
  1. เพิ่ม D1 สำหรับเก็บข้อมูล ใจจริงผมอยากใช้ Google Sheet api มากแต่ใน cloudflare worker ด้วยความที่เป็น edge เลยใช้บางฟังก์ชันของ Node.js ไม่ได้ (เช่นอัลกอริทึมเข้ารหัสหลาย ๆ ตัว) ไม่เป็นไรใช้ D1 ก็ดีเหมือนกัน
pnpm wrangler d1 create banks-transactions

ตรงนี้จะได้ database_name binding และ database_id มา อย่าลืมเอาไปเซฟใน wrangler.toml ด้วย

  1. เซ็ตอัปฐานข้อมูล สร้างไฟล์ schema.sql ที่มีเนื้อหาเป็นไฟล์ SQL ระบุโครงสร้างตารางที่จะใช้ เคสผมผมเน้นเก็บเนื้อหาอีเมล เลยจะใช้แค่ตารางเดียว
/* ./schema.sql */

DROP TABLE IF EXISTS transactions;
CREATE TABLE IF NOT EXISTS transactions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  sender VARCHAR(255) NOT NULL,
  content TEXT NOT NULL,
  metadata TEXT NOT NULL
);

แล้ว apply ทั้ง remote และ local ด้วยคำสั่ง

pnpx wrangler d1 execute banks-transactions --local --file=./schema.sql
pnpx wrangler d1 execute banks-transactions --remote --file=./schema.sql

[!note] ด้วยวิธีนี้จะสามารถรันคำสั่ง SQL ได้ทั้งใน local และ remote ด้วย pnpx wrangler d1 execute banks-transactions --local --command="SELECT * FROM transactions"

  1. มาถึงพระเอกจริง ๆ ของงาน โค้ดสำหรับให้ Worker จัดการอีเมลของเรา อย่าลืมติดตั้ง postal-mime ด้วย pnpm i postal-mime ก่อน
// ./src/index.ts
import PostalMime, { type Email } from 'postal-mime';

export default {
	async email(message, env, ctx) {
		const email = await PostalMime.parse(message.raw);

		function randomId() {
			return Math.random().toString(36).substring(7);
		}

		try {
			const metadata = {
				cc: email.cc,
				bcc: email.bcc,
				subject: email.subject,
				date: email.date,
				headers: email.headers,
				from: email.from,
				messageId: email.messageId,
				attachments: [],
				deliveredTo: email.deliveredTo,
				to: email.to,
				inReplyTo: email.inReplyTo,
				references: email.references,
				replyTo: email.replyTo,
				returnPath: email.returnPath,
				sender: email.sender,
			} satisfies Email;

			const stmt = env.DB.prepare("INSERT INTO transactions (sender, content, metadata) VALUES (?1, ?2, ?3)");
			await stmt.bind(message.from, email.html ?? email.text, JSON.stringify(metadata)).run();
		} catch (err) {
			console.error(err);
		}

		await message.forward(env.FORWARD);
	}
} satisfies ExportedHandler<Env>;

แต่ยังไม่เสร็จ คุณอย่าลืมไปตั้งค่า Destination address ให้เรียบร้อยก่อน ไม่งั้นอีเมลจะส่งไม่ได้ แล้วใส่ FORWARD=your_dest@mail.com ใน .dev.vars ด้วย

แล้วยัด var นี้ใน secret ด้วย wrangler secret put FORWARD แล้วกรอกอีเมลที่ว่า

  1. สุดท้ายก็เป็นการ deploy และเช็คว่าทำงานได้หรือไม่
pnpm wrangler deploy
  1. จัดแจงแก้อีเมลที่ให้ธนาคารส่งอีเมลหา ซึ่งคุณควรจะได้อีเมลเป็นปกติ เพิ่มเติมคือมีฐานข้อมูลที่เก็บข้อมูลอีเมลที่ส่งมา แล้วสามารถเข้าถึงได้จาก D1 ที่เราสร้างไว้ อย่าเผลอสร้าง endpoint อะไรเปิดข้อมูลนี้ให้คนอื่นเข้าถึงได้แล้วกัน

ด้วยสมัยนี้ผมเชื่อเหลือเกินว่า AI สามารถช่วยคุณได้เลยเขียนคร่าว ๆ แห่ะ ๆ เมื่อไหร่ที่ต้องการจะเข้าถึงข้อมูลที่เก็บก็อ่านจาก D1 ที่ว่าเอา วันนี้พอแค่นี้ก่อนดีกว่า ผมเพิ่งทำเสร็จ ถ้าลองแยกข้อมูลสำหรับ import เข้า cashew แล้วเดี๋ยวมาแชร์ต่อตอนถัดไป…