package top.r3944realms.ltdmanager.module import jakarta.mail.* import jakarta.mail.internet.InternetAddress import jakarta.mail.internet.MimeMessage import top.r3944realms.ltdmanager.core.mail.Mail import top.r3944realms.ltdmanager.utils.LoggerUtil import java.util.* import java.util.concurrent.LinkedBlockingQueue import kotlin.concurrent.thread class MailModule( moduleName: String, private val protocol: String = "SMTP", private val host: String, private val port: Int, private val senderEmailAddress: String, private val authToken: String, private val enableAuth: Boolean = true, private val enableTLS: Boolean = true, private val intervalMillis: Long = 2000L // 每封邮件之间的间隔(默认 2s) ) : BaseModule("MailModule", moduleName) { private lateinit var session: Session private val queue = LinkedBlockingQueue() // 邮件队列 private var workerThread: Thread? = null @Volatile private var running = false override fun onLoad() { LoggerUtil.logger.info("[$name] 模块加载中,初始化邮件会话...") /* 163 邮箱(以及大多数 SMTP 服务商)区别是: 465 👉 “隐式 SSL”,必须启用 mail.smtp.ssl.enable=true。 587 👉 “明文 + STARTTLS”,必须启用 mail.smtp.starttls.enable=true。 而注释中 onLoad() 写死了: put("mail.smtp.starttls.enable", enableTLS) 所以当用 465 端口时,服务端要求立即握手 SSL,但程序还在用明文 STARTTLS,直接就被 EOF 掉了。 * */ // val props = Properties().apply { // put("mail.transport.protocol", protocol) // put("mail.smtp.host", host) // put("mail.smtp.port", port) // put("mail.smtp.auth", enableAuth) // put("mail.smtp.starttls.enable", enableTLS) // } val props = Properties().apply { put("mail.transport.protocol", protocol) put("mail.smtp.host", host) put("mail.smtp.port", port) put("mail.smtp.auth", enableAuth) when (port) { 465 -> { // 隐式 SSL put("mail.smtp.ssl.enable", "true") put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory") put("mail.smtp.socketFactory.port", port.toString()) } 587 -> { // STARTTLS if (enableTLS) { put("mail.smtp.starttls.enable", "true") put("mail.smtp.starttls.required", "true") } } else -> { // 普通 25 端口或其他情况 if (enableTLS) { put("mail.smtp.starttls.enable", "true") } } } } session = Session.getInstance(props, object : Authenticator() { override fun getPasswordAuthentication(): PasswordAuthentication { return PasswordAuthentication(senderEmailAddress, authToken) } }) running = true workerThread = thread(start = true, name = "MailSender-Worker") { LoggerUtil.logger.info("[$name] 邮件发送线程启动") while (running && loaded) { try { val mail = queue.take() // 阻塞等待新任务 LoggerUtil.logger.info("[$name] 开始发送邮件 -> 收件人: ${mail.to.joinToString(",")}") sendInternal(mail) LoggerUtil.logger.info("[$name] 邮件发送完成 -> ${mail.to.joinToString(",")}") Thread.sleep(intervalMillis) // 限流 } catch (e: InterruptedException) { LoggerUtil.logger.info("[$name] 邮件发送线程被中断,准备退出") break } catch (e: Exception) { LoggerUtil.logger.error("[$name] 邮件发送出现异常", e) } } } } override suspend fun onUnload() { LoggerUtil.logger.info("[$name] 模块卸载,停止邮件发送线程") running = false workerThread?.interrupt() workerThread = null } /** * 加入发送队列 */ fun enqueue(mail: Mail) { if (!loaded) throw IllegalStateException("MailModule 未加载,不能发送邮件") LoggerUtil.logger.info("[$name] 邮件加入队列 -> 收件人: ${mail.to.joinToString(",")}, 主题: ${mail.subject}") queue.put(mail) } /** * 真正的发送逻辑(内部调用) */ private fun sendInternal(mail: Mail) { val message = MimeMessage(session).apply { setFrom(InternetAddress(senderEmailAddress,mail.from ?: senderEmailAddress, "UTF-8")) setRecipients(Message.RecipientType.TO, mail.to.joinToString(",")) if (mail.cc.isNotEmpty()) { setRecipients(Message.RecipientType.CC, mail.cc.joinToString(",")) } if (mail.bcc.isNotEmpty()) { setRecipients(Message.RecipientType.BCC, mail.bcc.joinToString(",")) } subject = mail.subject setContent( mail.body, if (mail.isHtml) "text/html;charset=UTF-8" else "text/plain;charset=UTF-8" ) } Transport.send(message) } override fun info(): String { return buildString { appendLine("[$name] 邮件发送模块") appendLine("功能: 异步发送邮件,支持收件人/抄送/密送,支持 HTML 或纯文本邮件。") appendLine("SMTP 配置:") appendLine(" - 协议: $protocol") appendLine(" - 主机: $host") appendLine(" - 端口: $port") appendLine(" - 发件人邮箱: $senderEmailAddress") appendLine(" - 身份认证: ${if (enableAuth) "启用" else "禁用"}") appendLine(" - TLS/SSL: ${if (enableTLS) "启用" else "禁用"}") appendLine("队列行为:") appendLine(" - 邮件发送间隔: ${intervalMillis}ms") appendLine(" - 队列长度: ${queue.size}") appendLine(" - 当前发送线程状态: ${if (workerThread?.isAlive == true) "运行中" else "未运行"}") } } override fun help(): String { return buildString { appendLine("📖 [$name] 使用帮助:") appendLine("1. 创建 Mail 对象,设置收件人、主题和正文") appendLine(" 例如: Mail(to = listOf(\"example@mail.com\"), subject = \"测试\", body = \"Hello\")") appendLine("2. 调用 enqueue(mail) 加入发送队列") appendLine(" 邮件将异步发送,间隔 $intervalMillis ms") appendLine("3. 模块卸载时会自动停止发送线程") appendLine() appendLine("注意:") appendLine(" - 确保 SMTP 配置正确,否则发送失败") appendLine(" - 发件人邮箱需要允许 SMTP/授权码登录") } } }