新增自定义显示TG邮件消息内容

This commit is contained in:
eoao
2025-10-23 00:22:45 +08:00
parent 57638aa8e1
commit aded699314
10 changed files with 118 additions and 61 deletions

View File

@@ -87,16 +87,11 @@ function autoScale() {
if (!shadowContent) return if (!shadowContent) return
const parentWidth = parent.offsetWidth const parentWidth = parent.offsetWidth
const parentHeight = parent.offsetHeight
const childWidth = shadowContent.scrollWidth const childWidth = shadowContent.scrollWidth
const childHeight = shadowContent.scrollHeight
if (childWidth === 0 || childHeight === 0) return if (childWidth === 0) return
const scaleX = parentWidth / childWidth const scale = parentWidth / childWidth
const scaleY = parentHeight / childHeight
const scale = Math.min(scaleX, scaleY)
const hostElement = shadowRoot.host const hostElement = shadowRoot.host
hostElement.style.zoom = scale hostElement.style.zoom = scale

View File

@@ -137,7 +137,7 @@ const en = {
websiteReg: 'Sign Up', websiteReg: 'Sign Up',
loginDomain: 'Sign-In Box Domain', loginDomain: 'Sign-In Box Domain',
multipleEmail: 'Multiple Accounts', multipleEmail: 'Multiple Accounts',
multipleEmailDesc: 'Enable this feature to allow users to add multiple accounts.', multipleEmailDesc: 'Enable this feature to allow users to add multiple accounts',
customization: 'Customization', customization: 'Customization',
websiteTitle: 'Title', websiteTitle: 'Title',
loginBoxOpacity: 'Login Box Opacity', loginBoxOpacity: 'Login Box Opacity',
@@ -145,7 +145,7 @@ const en = {
emailSetting: 'Email', emailSetting: 'Email',
receiveEmail: 'Receive Email', receiveEmail: 'Receive Email',
autoRefresh: 'Auto Refresh', autoRefresh: 'Auto Refresh',
autoRefreshDesc: 'Automatically fetch the latest emails from the server.', autoRefreshDesc: 'Automatically fetch the latest emails from the server',
sendEmail: 'Send Email', sendEmail: 'Send Email',
resendToken: 'Resend Token', resendToken: 'Resend Token',
oss: 'Object Storage', oss: 'Object Storage',
@@ -165,7 +165,7 @@ const en = {
version: 'Version', version: 'Version',
community: 'Community', community: 'Community',
changeTitle: 'Change Title', changeTitle: 'Change Title',
addResendTokenDesc: 'Input to add; leave empty to delete.', addResendTokenDesc: 'Input to add; leave empty to delete',
addOsDomain: 'Add Domain', addOsDomain: 'Add Domain',
domainDesc: 'Domain', domainDesc: 'Domain',
addTurnstileSecret: 'Add turnstile secret', addTurnstileSecret: 'Add turnstile secret',
@@ -173,14 +173,14 @@ const en = {
tgBotDesc: 'Forward received emails to a Telegram bot', tgBotDesc: 'Forward received emails to a Telegram bot',
tgBotToken: 'Bot token', tgBotToken: 'Bot token',
toBotTokenDesc: 'Multiple user chat_ids, separated by commas', toBotTokenDesc: 'Multiple user chat_ids, separated by commas',
otherEmailDesc: 'emails can be forwarded to external email, but must be verified via cloudflare.', otherEmailDesc: 'emails can be forwarded to external email, but must be verified via cloudflare',
otherEmailInputDesc: 'Separate multiple email addresses with commas.', otherEmailInputDesc: 'Separate multiple email addresses with commas',
forwardingRulesDesc: 'Rule-based forwarding only forwards emails received by the specified address.', forwardingRulesDesc: 'Rule-based forwarding only forwards emails received by the specified address',
ruleEmailsInputDesc: 'Separate multiple email addresses with commas.', ruleEmailsInputDesc: 'Separate multiple email addresses with commas',
resendTokenList: 'Token List', resendTokenList: 'Token List',
domain: 'Domain', domain: 'Domain',
optional: 'Optional', optional: 'Optional',
subjectInputDesc: 'Please enter the email subject.', subjectInputDesc: 'Please enter the email subject',
changeUserName: 'Change Username', changeUserName: 'Change Username',
sendSeparately: 'Separately', sendSeparately: 'Separately',
send: 'Send', send: 'Send',
@@ -251,14 +251,14 @@ const en = {
supportDesc: 'Buy me tea', supportDesc: 'Buy me tea',
featDesc: 'Feature Description', featDesc: 'Feature Description',
emailInterception: 'Email Interception', emailInterception: 'Email Interception',
emailInterceptionDesc: 'Enter a domain or email address to prevent users from receiving emails from certain websites (Enter * to block all).', emailInterceptionDesc: 'Enter a domain or email address to prevent users from receiving emails from certain websites (Enter * to block all)',
availableDomains: 'Available Domains', availableDomains: 'Available Domains',
availableDomainsDesc: 'Restrict users to email domains specified. Domains not on the approved list will be blocked from registration, adding email addresses, and sending/receiving emails. If left blank, all domains will be allowed by default.', availableDomainsDesc: 'Restrict users to email domains specified. Domains not on the approved list will be blocked from registration, adding email addresses, and sending/receiving emails. If left blank, all domains will be allowed by default',
backgroundUrlDesc: 'Image URL', backgroundUrlDesc: 'Image URL',
localUpload: ' Local upload', localUpload: ' Local upload',
imageLink: 'Image URL', imageLink: 'Image URL',
imageLinkErrorMsg: 'Invalid image URL', imageLinkErrorMsg: 'Invalid image URL',
backgroundWarning: 'Image file size affects website load speed.', backgroundWarning: 'Image file size affects website load speed',
rulesVerify: 'Rules', rulesVerify: 'Rules',
rulesVerifyTitle: 'Trigger After {count} Daily Uses per IP', rulesVerifyTitle: 'Trigger After {count} Daily Uses per IP',
botVerifyMsg: 'Please verify that you are human', botVerifyMsg: 'Please verify that you are human',
@@ -278,7 +278,7 @@ const en = {
verifyModuleFailed: 'Verification module failed to load. Please refresh the page', verifyModuleFailed: 'Verification module failed to load. Please refresh the page',
popUp: 'Pop Up', popUp: 'Pop Up',
noRecipientTitle: 'No Recipient', noRecipientTitle: 'No Recipient',
noRecipientDesc: 'Emails can be received even without a registered email address.', noRecipientDesc: 'Emails can be received even without a registered email address',
preview: 'Preview', preview: 'Preview',
help: 'Help', help: 'Help',
document: 'Document', document: 'Document',
@@ -292,12 +292,17 @@ const en = {
include: 'Include', include: 'Include',
delAllEmailConfirm: 'Do you really want to delete it?', delAllEmailConfirm: 'Do you really want to delete it?',
s3Configuration: 'S3 Configuration', s3Configuration: 'S3 Configuration',
s3Desc: 'If another S3-compatible storage is configured, R2 will take priority if its already connected',
confirmDeletionOfContacts: 'Confirm clearing contacts?', confirmDeletionOfContacts: 'Confirm clearing contacts?',
recentContacts: 'Recent contacts', recentContacts: 'Recent contacts',
selectContacts: 'Select', selectContacts: 'Select',
forcePathStyleDesc: 'Some self-hosted object storages require path-style access to be enabled.', forcePathStyleDesc: 'Some self-hosted object storages require path-style access to be enabled',
kvStorageDesc: 'Replace object storage with KV, and update the access domain to a Worker custom domain.', kvStorageDesc: 'Replace object storage with KV, and update the access domain to a Worker custom domain',
kvStorage: 'KV Storage' kvStorage: 'KV Storage',
customDomainDesc: 'Worker custom domain',
show: 'Show',
hide: 'Hide',
onlyName: 'Only name'
} }
export default en export default en

View File

@@ -298,7 +298,10 @@ const zh = {
selectContacts: '选中', selectContacts: '选中',
forcePathStyleDesc: '路径样式访问,一些自建的对象存储需要打开', forcePathStyleDesc: '路径样式访问,一些自建的对象存储需要打开',
kvStorageDesc: '使用KV替代对象存储访问域名改成worker自定义域', kvStorageDesc: '使用KV替代对象存储访问域名改成worker自定义域',
kvStorage: 'KV存储', kvStorage: 'KV 存储',
customDomainDesc: 'Worker 自定义域' customDomainDesc: 'Worker 自定义域',
show: '显示',
hide: '隐藏',
onlyName: '仅名字'
} }
export default zh export default zh

View File

@@ -96,7 +96,7 @@
fit="cover" fit="cover"
> >
<template #error> <template #error>
<div class="error-image" @click="openSetBackground"> <div class="error-image">
<Icon icon="ph:image" width="24" height="24"/> <Icon icon="ph:image" width="24" height="24"/>
</div> </div>
</template> </template>
@@ -486,6 +486,28 @@
<el-input-tag tag-type="warning" :placeholder="$t('toBotTokenDesc')" v-model="tgChatId" <el-input-tag tag-type="warning" :placeholder="$t('toBotTokenDesc')" v-model="tgChatId"
@add-tag="addChatTag"></el-input-tag> @add-tag="addChatTag"></el-input-tag>
<el-input tag-type="warning" :placeholder="$t('customDomainDesc')" v-model="customDomain" ></el-input> <el-input tag-type="warning" :placeholder="$t('customDomainDesc')" v-model="customDomain" ></el-input>
<div class="tg-msg-label">
<span>{{t('from')}}</span>
<el-select v-model="tgMsgFrom" >
<el-option
v-for="item in tgMsgFromOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<div class="tg-msg-label">
<span>{{t('recipient')}}</span>
<el-select v-model="tgMsgTo" >
<el-option
v-for="item in tgMsgToOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@@ -787,6 +809,12 @@ const emailColumnWidth = ref(0)
const tokenColumnWidth = ref(0) const tokenColumnWidth = ref(0)
const ruleType = ref(0) const ruleType = ref(0)
const ruleEmail = ref([]) const ruleEmail = ref([])
const tgMsgFrom = ref('')
const tgMsgTo = ref('')
const tgMsgFromOption = [{label: t('show'), value: 'show'}, {label: t('hide'), value: 'hide'}, {label: t('onlyName'), value:'only-name'}]
const tgMsgToOption = [{label: t('show'), value: 'show'}, {label: t('hide'), value: 'hide'}]
const tgMsgLabelWidth = computed(() => locale.value === 'en' ? '120px' : '100px');
getSettings() getSettings()
getUpdate() getUpdate()
@@ -901,6 +929,8 @@ function openTgSetting() {
tgBotStatus.value = setting.value.tgBotStatus tgBotStatus.value = setting.value.tgBotStatus
tgBotToken.value = setting.value.tgBotToken tgBotToken.value = setting.value.tgBotToken
customDomain.value = setting.value.customDomain customDomain.value = setting.value.customDomain
tgMsgFrom.value = setting.value.tgMsgFrom
tgMsgTo.value = setting.value.tgMsgTo
tgChatId.value = [] tgChatId.value = []
if (setting.value.tgChatId) { if (setting.value.tgChatId) {
const list = setting.value.tgChatId.split(',') const list = setting.value.tgChatId.split(',')
@@ -1036,7 +1066,9 @@ function tgBotSave() {
tgBotToken: tgBotToken.value, tgBotToken: tgBotToken.value,
customDomain: customDomain.value, customDomain: customDomain.value,
tgBotStatus: tgBotStatus.value, tgBotStatus: tgBotStatus.value,
tgChatId: tgChatId.value + '' tgChatId: tgChatId.value + '',
tgMsgFrom: tgMsgFrom.value,
tgMsgTo: tgMsgTo.value
} }
editSetting(form) editSetting(form)
} }
@@ -1496,6 +1528,7 @@ function editSetting(settingForm, refreshStatus = true) {
.forward-set-title { .forward-set-title {
top: 1px; top: 1px;
padding-right: 5px;
position: relative; position: relative;
font-size: 16px; font-size: 16px;
font-weight: bold;; font-weight: bold;;
@@ -1548,11 +1581,24 @@ function editSetting(settingForm, refreshStatus = true) {
.forward-set-body { .forward-set-body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 15px;
.el-switch { .el-switch {
align-self: end; align-self: end;
} }
> *:nth-child(-n+2) {
margin-bottom: 15px;
}
.tg-msg-label {
margin-top: 10px;
display: flex;
align-items: center;
justify-content: space-between;
.el-select {
width: v-bind(tgMsgLabelWidth);
}
}
} }
.forward { .forward {

View File

@@ -6,3 +6,4 @@ app.get('/telegram/getEmail/:token', async (c) => {
c.header('Cache-Control', 'public, max-age=604800, immutable'); c.header('Cache-Control', 'public, max-age=604800, immutable');
return c.html(content) return c.html(content)
}); });

View File

@@ -42,6 +42,8 @@ export const setting = sqliteTable('setting', {
s3SecretKey: text('s3_secret_key').default('').notNull(), s3SecretKey: text('s3_secret_key').default('').notNull(),
kvStorage: integer('kv_storage').default(1).notNull(), kvStorage: integer('kv_storage').default(1).notNull(),
forcePathStyle: integer('force_path_style').default(1).notNull(), forcePathStyle: integer('force_path_style').default(1).notNull(),
customDomain: text('custom_domain').default('').notNull() customDomain: text('custom_domain').default('').notNull(),
tgMsgFrom: text('tg_msg_from').default('only-name').notNull(),
tgMsgTo: text('tg_msg_to').default('show').notNull()
}); });
export default setting export default setting

View File

@@ -32,7 +32,9 @@ const init = {
await c.env.db.batch([ await c.env.db.batch([
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN force_path_style INTEGER NOT NULL DEFAULT 1;`), c.env.db.prepare(`ALTER TABLE setting ADD COLUMN force_path_style INTEGER NOT NULL DEFAULT 1;`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN kv_storage INTEGER NOT NULL DEFAULT 1;`), c.env.db.prepare(`ALTER TABLE setting ADD COLUMN kv_storage INTEGER NOT NULL DEFAULT 1;`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN custom_domain TEXT NOT NULL DEFAULT '';`) c.env.db.prepare(`ALTER TABLE setting ADD COLUMN custom_domain TEXT NOT NULL DEFAULT '';`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN tg_msg_to TEXT NOT NULL DEFAULT 'show';`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN tg_msg_from TEXT NOT NULL DEFAULT 'only-name';`)
]); ]);
} catch (e) { } catch (e) {
console.error(e) console.error(e)

View File

@@ -15,26 +15,6 @@ import verifyUtils from '../utils/verify-utils';
const telegramService = { const telegramService = {
async getEmailById(c, params) {
const { emailId } = params
const emailRow = await orm(c).select().from(email).where(eq(email.emailId, emailId)).get();
if (emailRow) {
if (emailRow.content) {
return emailHtmlTemplate(emailRow.content || '')
} else {
return emailTextTemplate(emailRow.text || '')
}
} else {
return emailTextTemplate('The email does not exist')
}
},
async getEmailContent(c, params) { async getEmailContent(c, params) {
const { token } = params const { token } = params
@@ -50,7 +30,8 @@ const telegramService = {
if (emailRow) { if (emailRow) {
if (emailRow.content) { if (emailRow.content) {
return emailHtmlTemplate(emailRow.content || '') const { r2Domain } = await settingService.query(c);
return emailHtmlTemplate(emailRow.content || '', r2Domain)
} else { } else {
return emailTextTemplate(emailRow.text || '') return emailTextTemplate(emailRow.text || '')
} }

View File

@@ -1,10 +1,12 @@
import { parseHTML } from 'linkedom'; import { parseHTML } from 'linkedom';
import domainUtils from '../utils/domain-uitls';
export default function emailHtmlTemplate(html) { export default function emailHtmlTemplate(html, domain) {
const { document } = parseHTML(html); const { document } = parseHTML(html);
document.querySelectorAll('script').forEach(script => script.remove()); document.querySelectorAll('script').forEach(script => script.remove());
html = document.documentElement.outerHTML; html = document.documentElement.outerHTML;
html = html.replace(/{{domain}}/g, domainUtils.toOssDomain(domain) + '/');
return `<!DOCTYPE html> return `<!DOCTYPE html>
<html lang='en' > <html lang='en' >
@@ -105,6 +107,7 @@ export default function emailHtmlTemplate(html) {
} }
function autoScale(shadowRoot, container) { function autoScale(shadowRoot, container) {
if (!shadowRoot || !container) return; if (!shadowRoot || !container) return;
const parent = container; const parent = container;
@@ -113,16 +116,11 @@ export default function emailHtmlTemplate(html) {
if (!shadowContent) return; if (!shadowContent) return;
const parentWidth = parent.offsetWidth; const parentWidth = parent.offsetWidth;
const parentHeight = parent.offsetHeight;
const childWidth = shadowContent.scrollWidth; const childWidth = shadowContent.scrollWidth;
const childHeight = shadowContent.scrollHeight;
if (childWidth === 0 || childHeight === 0) return; if (childWidth === 0) return;
const scaleX = parentWidth / childWidth; const scale = parentWidth / childWidth;
const scaleY = parentHeight / childHeight;
const scale = Math.min(scaleX, scaleY);
const hostElement = shadowRoot.host; const hostElement = shadowRoot.host;
hostElement.style.zoom = scale; hostElement.style.zoom = scale;

View File

@@ -1,7 +1,31 @@
export default function emailMsgTemplate(email) { export default function emailMsgTemplate(email, tgMsgTo, tgMsgFrom) {
return `<b>${email.subject}</b>
let template = `<b>${email.subject}</b>`
if (tgMsgFrom === 'only-name') {
template += `
发件人:${email.name}`
}
if (tgMsgFrom === 'show') {
template += `
发件人:${email.name} &lt;${email.sendEmail}&gt;`
}
if(tgMsgTo === 'show' && tgMsgFrom === 'hide') {
template += `
发件人:${email.name} &lt;${email.sendEmail}&gt;
收件人:\u200B${email.toEmail}` 收件人:\u200B${email.toEmail}`
return template
}
if(tgMsgTo === 'show') {
template += `
收件人:\u200B${email.toEmail}`
}
return template;
} }