fix(tci): subscribe to the radio's meters; fix the new-spot count; explain an SMTP refusal
TCI meters: the S-meter came only from RX_SMETER and the transmit meters from TX_POWER / TX_SWR — commands ExpertSDR3 does not send. The protocol's answer is a subscription (RX_SENSORS_ENABLE / TX_SENSORS_ENABLE, §4.4 of the TCI PDF), after which the radio pushes RX_CHANNEL_SENSORS and TX_SENSORS. Nobody had asked, so the console's meters sat empty in RX and in TX while everything else worked. Cluster: the held-spot counter looked for the row it froze on. A station spotted again REPLACES its row, so that row vanishes in the ordinary course of things and the count fell through to 'everything is new' — 4, 5, then 500. Counted by timestamp now, which survives both the replacement and the ring buffer. SMTP: '535 5.7.139 basic authentication is disabled' is a policy, not a typo. The message now says so, and says what actually helps, without claiming a policy when the server simply rejected the password.
This commit is contained in:
+33
-2
@@ -5,6 +5,7 @@ package email
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wneessen/go-mail"
|
||||
@@ -86,12 +87,42 @@ func SendFiles(cfg Config, to, subject, body string, attachPaths []string) error
|
||||
return fmt.Errorf("smtp client: %w", err)
|
||||
}
|
||||
if err := client.DialAndSend(m); err != nil {
|
||||
return fmt.Errorf("send via %s:%d (%s, %s): %w",
|
||||
cfg.Host, cfg.Port, cfg.Encryption, describeSize(attachPaths), err)
|
||||
return fmt.Errorf("send via %s:%d (%s, %s): %w%s",
|
||||
cfg.Host, cfg.Port, cfg.Encryption, describeSize(attachPaths), err, explainSMTP(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// explainSMTP turns a server's refusal into the thing to go and do.
|
||||
//
|
||||
// A rejection is quoted verbatim above it — the server's own words are the
|
||||
// evidence — but several of them name a policy rather than a mistake, and no
|
||||
// amount of re-checking the password will fix those. Microsoft's is the one
|
||||
// operators keep hitting: basic authentication for SMTP is switched off across
|
||||
// Microsoft 365 and outlook.com, and an app password does not bring it back.
|
||||
func explainSMTP(err error) string {
|
||||
msg := strings.ToLower(err.Error())
|
||||
switch {
|
||||
case strings.Contains(msg, "basic authentication is disabled"),
|
||||
strings.Contains(msg, "5.7.139"):
|
||||
return "\n\nMicrosoft has switched off password-based SMTP for this account. " +
|
||||
"An app password does not restore it — the server refuses the password itself, not the one you typed. " +
|
||||
"On a Microsoft 365 tenant an administrator can re-enable it for this mailbox " +
|
||||
"(Set-CASMailbox -SmtpClientAuthenticationDisabled $false, plus the tenant-wide setting); " +
|
||||
"otherwise use another provider for alerts (a Gmail account with an app password works, so does any ordinary IMAP/SMTP host)."
|
||||
case strings.Contains(msg, "application-specific password"),
|
||||
strings.Contains(msg, "5.7.9"):
|
||||
return "\n\nThis account needs an APP PASSWORD rather than the one you sign in with " +
|
||||
"(Google, Yahoo and others require it once two-factor authentication is on)."
|
||||
case strings.Contains(msg, "5.7.8"), strings.Contains(msg, "authentication failed"),
|
||||
strings.Contains(msg, "535"):
|
||||
return "\n\nThe server rejected the username or the password."
|
||||
case strings.Contains(msg, "must issue a starttls"):
|
||||
return "\n\nThe server requires encryption: set STARTTLS (usually port 587) or SSL (465)."
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// describeSize reports what was attached, in bytes.
|
||||
//
|
||||
// "An existing connection was forcibly closed" during DATA is the same message
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExplainSMTP(t *testing.T) {
|
||||
// The real refusal, from an operator's Outlook account.
|
||||
outlook := errors.New("SMTP AUTH failed: 535 5.7.139 Authentication unsuccessful, basic authentication is disabled.")
|
||||
if got := explainSMTP(outlook); !strings.Contains(got, "Microsoft has switched off") {
|
||||
t.Errorf("the Microsoft policy refusal is not explained: %q", got)
|
||||
}
|
||||
// A plain wrong password must NOT claim a policy: the advice would send the
|
||||
// operator to an administrator over a typo.
|
||||
wrong := errors.New("535 5.7.8 authentication failed")
|
||||
got := explainSMTP(wrong)
|
||||
if strings.Contains(got, "Microsoft") {
|
||||
t.Errorf("a wrong password was explained as a Microsoft policy: %q", got)
|
||||
}
|
||||
if !strings.Contains(got, "rejected the username") {
|
||||
t.Errorf("a wrong password is not explained: %q", got)
|
||||
}
|
||||
// Anything else is left to speak for itself.
|
||||
if got := explainSMTP(errors.New("dial tcp: i/o timeout")); got != "" {
|
||||
t.Errorf("an unrelated error got an explanation: %q", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user