Telegram: Send messages via telegram

Most messages are now sent as plaintext via telegram when suitable.
This commit is contained in:
Harvey Tindall 2021-05-07 16:06:47 +01:00
parent 72bf280e2d
commit 716d6a931a
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
10 changed files with 118 additions and 112 deletions

104
api.go
View File

@ -282,7 +282,7 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
} }
} }
app.jf.CacheExpiry = time.Now() app.jf.CacheExpiry = time.Now()
if app.config.Section("password_resets").Key("enabled").MustBool(false) { if emailEnabled {
app.storage.emails[id] = req.Email app.storage.emails[id] = req.Email
app.storage.storeEmails() app.storage.storeEmails()
} }
@ -500,15 +500,16 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
} }
} }
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" { if (emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "") || telegramTokenIndex != -1 {
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email) name := app.getAddressOrName(user.ID)
app.debug.Printf("%s: Sending welcome message to %s", req.Username, name)
msg, err := app.email.constructWelcome(req.Username, expiry, app, false) msg, err := app.email.constructWelcome(req.Username, expiry, app, false)
if err != nil { if err != nil {
app.err.Printf("%s: Failed to construct welcome email: %v", req.Username, err) app.err.Printf("%s: Failed to construct welcome message: %v", req.Username, err)
} else if err := app.email.send(msg, req.Email); err != nil { } else if err := app.sendByID(msg, user.ID); err != nil {
app.err.Printf("%s: Failed to send welcome email: %v", req.Username, err) app.err.Printf("%s: Failed to send welcome message: %v", req.Username, err)
} else { } else {
app.info.Printf("%s: Sent welcome email to \"%s\"", req.Username, req.Email) app.info.Printf("%s: Sent welcome message to \"%s\"", req.Username, name)
} }
} }
app.jf.CacheExpiry = time.Now() app.jf.CacheExpiry = time.Now()
@ -575,7 +576,20 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
"GetUser": map[string]string{}, "GetUser": map[string]string{},
"SetPolicy": map[string]string{}, "SetPolicy": map[string]string{},
} }
var addresses []string sendMail := emailEnabled || app.config.Section("telegram").Key("enabled").MustBool(false)
var msg *Message
var err error
if sendMail {
if req.Enabled {
msg, err = app.email.constructEnabled(req.Reason, app, false)
} else {
msg, err = app.email.constructDisabled(req.Reason, app, false)
}
if err != nil {
app.err.Printf("Failed to construct account enabled/disabled emails: %v", err)
sendMail = false
}
}
for _, userID := range req.Users { for _, userID := range req.Users {
user, status, err := app.jf.UserByID(userID, false) user, status, err := app.jf.UserByID(userID, false)
if status != 200 || err != nil { if status != 200 || err != nil {
@ -590,31 +604,13 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
app.err.Printf("Failed to set policy for user \"%s\" (%d): %v", userID, status, err) app.err.Printf("Failed to set policy for user \"%s\" (%d): %v", userID, status, err)
continue continue
} }
if emailEnabled && req.Notify { if sendMail && req.Notify {
addr, ok := app.storage.emails[userID] if err := app.sendByID(msg, userID); err != nil {
if addr != nil && ok { app.err.Printf("Failed to send account enabled/disabled email: %v", err)
addresses = append(addresses, addr.(string)) continue
} }
} }
} }
if len(addresses) != 0 {
go func(reason string, addresses []string) {
var msg *Message
var err error
if req.Enabled {
msg, err = app.email.constructEnabled(reason, app, false)
} else {
msg, err = app.email.constructDisabled(reason, app, false)
}
if err != nil {
app.err.Printf("Failed to construct account enabled/disabled emails: %v", err)
} else if err := app.email.send(msg, addresses...); err != nil {
app.err.Printf("Failed to send account enabled/disabled emails: %v", err)
} else {
app.info.Println("Sent account enabled/disabled emails")
}
}(req.Reason, addresses)
}
app.jf.CacheExpiry = time.Now() app.jf.CacheExpiry = time.Now()
if len(errors["GetUser"]) != 0 || len(errors["SetPolicy"]) != 0 { if len(errors["GetUser"]) != 0 || len(errors["SetPolicy"]) != 0 {
gc.JSON(500, errors) gc.JSON(500, errors)
@ -634,10 +630,19 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
// @tags Users // @tags Users
func (app *appContext) DeleteUsers(gc *gin.Context) { func (app *appContext) DeleteUsers(gc *gin.Context) {
var req deleteUserDTO var req deleteUserDTO
var addresses []string
gc.BindJSON(&req) gc.BindJSON(&req)
errors := map[string]string{} errors := map[string]string{}
ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false) ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false)
sendMail := emailEnabled || app.config.Section("telegram").Key("enabled").MustBool(false)
var msg *Message
var err error
if sendMail {
msg, err = app.email.constructDeleted(req.Reason, app, false)
if err != nil {
app.err.Printf("Failed to construct account deletion emails: %v", err)
sendMail = false
}
}
for _, userID := range req.Users { for _, userID := range req.Users {
if ombiEnabled { if ombiEnabled {
ombiUser, code, err := app.getOmbiUser(userID) ombiUser, code, err := app.getOmbiUser(userID)
@ -660,25 +665,12 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
errors[userID] += msg errors[userID] += msg
} }
} }
if emailEnabled && req.Notify { if sendMail && req.Notify {
addr, ok := app.storage.emails[userID] if err := app.sendByID(msg, userID); err != nil {
if addr != nil && ok { app.err.Printf("Failed to send account deletion email: %v", err)
addresses = append(addresses, addr.(string))
} }
} }
} }
if len(addresses) != 0 {
go func(reason string, addresses []string) {
msg, err := app.email.constructDeleted(reason, app, false)
if err != nil {
app.err.Printf("Failed to construct account deletion emails: %v", err)
} else if err := app.email.send(msg, addresses...); err != nil {
app.err.Printf("Failed to send account deletion emails: %v", err)
} else {
app.info.Println("Sent account deletion emails")
}
}(req.Reason, addresses)
}
app.jf.CacheExpiry = time.Now() app.jf.CacheExpiry = time.Now()
if len(errors) == len(req.Users) { if len(errors) == len(req.Users) {
respondBool(500, false, gc) respondBool(500, false, gc)
@ -735,29 +727,21 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
func (app *appContext) Announce(gc *gin.Context) { func (app *appContext) Announce(gc *gin.Context) {
var req announcementDTO var req announcementDTO
gc.BindJSON(&req) gc.BindJSON(&req)
if !emailEnabled { if !(emailEnabled || app.config.Section("telegram").Key("enabled").MustBool(false)) {
respondBool(400, false, gc) respondBool(400, false, gc)
return return
} }
addresses := []string{}
for _, userID := range req.Users {
addr, ok := app.storage.emails[userID]
if !ok || addr == "" {
continue
}
addresses = append(addresses, addr.(string))
}
msg, err := app.email.constructTemplate(req.Subject, req.Message, app) msg, err := app.email.constructTemplate(req.Subject, req.Message, app)
if err != nil { if err != nil {
app.err.Printf("Failed to construct announcement emails: %v", err) app.err.Printf("Failed to construct announcement messages: %v", err)
respondBool(500, false, gc) respondBool(500, false, gc)
return return
} else if err := app.email.send(msg, addresses...); err != nil { } else if err := app.sendByID(msg, req.Users...); err != nil {
app.err.Printf("Failed to send announcement emails: %v", err) app.err.Printf("Failed to send announcement messages: %v", err)
respondBool(500, false, gc) respondBool(500, false, gc)
return return
} }
app.info.Printf("Sent announcement email to %d users", len(addresses)) app.info.Println("Sent announcement messages")
respondBool(200, true, gc) respondBool(200, true, gc)
} }

View File

@ -755,3 +755,30 @@ func (emailer *Emailer) constructUserExpired(app *appContext, noSub bool) (*Mess
func (emailer *Emailer) send(email *Message, address ...string) error { func (emailer *Emailer) send(email *Message, address ...string) error {
return emailer.sender.Send(emailer.fromName, emailer.fromAddr, email, address...) return emailer.sender.Send(emailer.fromName, emailer.fromAddr, email, address...)
} }
func (app *appContext) sendByID(email *Message, ID ...string) error {
tgEnabled := app.config.Section("telegram").Key("enabled").MustBool(false)
for _, id := range ID {
var err error
if tgChat, ok := app.storage.telegram[id]; ok && tgChat.Contact && tgEnabled {
err = app.telegram.Send(email, tgChat.ChatID)
} else if address, ok := app.storage.emails[id]; ok {
err = app.email.send(email, address.(string))
}
if err != nil {
return err
}
}
return nil
}
func (app *appContext) getAddressOrName(jfID string) string {
tgEnabled := app.config.Section("telegram").Key("enabled").MustBool(false)
if tgChat, ok := app.storage.telegram[jfID]; ok && tgChat.Contact && tgEnabled {
return "@" + tgChat.Username
}
if addr, ok := app.storage.emails[jfID]; ok {
return addr.(string)
}
return ""
}

View File

@ -6,6 +6,7 @@
window.URLBase = "{{ .urlBase }}"; window.URLBase = "{{ .urlBase }}";
window.notificationsEnabled = {{ .notifications }}; window.notificationsEnabled = {{ .notifications }};
window.emailEnabled = {{ .email_enabled }}; window.emailEnabled = {{ .email_enabled }};
window.telegramEnabled = {{ .telegram_enabled }};
window.ombiEnabled = {{ .ombiEnabled }}; window.ombiEnabled = {{ .ombiEnabled }};
window.usernameEnabled = {{ .username }}; window.usernameEnabled = {{ .username }};
window.langFile = JSON.parse({{ .language }}); window.langFile = JSON.parse({{ .language }});

View File

@ -69,27 +69,24 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
return return
} }
app.storage.loadEmails() app.storage.loadEmails()
var address string
uid := user.ID uid := user.ID
if uid == "" { if uid == "" {
app.err.Printf("Couldn't get user ID for user \"%s\"", pwr.Username) app.err.Printf("Couldn't get user ID for user \"%s\"", pwr.Username)
return return
} }
addr, ok := app.storage.emails[uid] name := app.getAddressOrName(uid)
if !ok || addr == nil { if name != "" {
app.err.Printf("Couldn't find email for user \"%s\". Make sure it's set", pwr.Username) msg, err := app.email.constructReset(pwr, app, false)
return
} if err != nil {
address = addr.(string) app.err.Printf("Failed to construct password reset message for %s", pwr.Username)
msg, err := app.email.constructReset(pwr, app, false) app.debug.Printf("%s: Error: %s", pwr.Username, err)
if err != nil { } else if err := app.sendByID(msg, uid); err != nil {
app.err.Printf("Failed to construct password reset email for %s", pwr.Username) app.err.Printf("Failed to send password reset message to \"%s\"", name)
app.debug.Printf("%s: Error: %s", pwr.Username, err) app.debug.Printf("%s: Error: %s", pwr.Username, err)
} else if err := app.email.send(msg, address); err != nil { } else {
app.err.Printf("Failed to send password reset email to \"%s\"", address) app.info.Printf("Sent password reset message to \"%s\"", name)
app.debug.Printf("%s: Error: %s", pwr.Username, err) }
} else {
app.info.Printf("Sent password reset email to \"%s\"", address)
} }
} else { } else {
app.err.Printf("Password reset for user \"%s\" has already expired (%s). Check your time settings.", pwr.Username, pwr.Expiry) app.err.Printf("Password reset for user \"%s\" has already expired (%s). Check your time settings.", pwr.Username, pwr.Expiry)

View File

@ -3,7 +3,6 @@ package main
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"strconv"
"strings" "strings"
"time" "time"
@ -194,15 +193,11 @@ func (t *TelegramDaemon) QuoteReply(upd *tg.Update, content string) error {
return err return err
} }
// Send adds compatibility with EmailClient, fromName/fromAddr are discarded, message.Text is used, addresses are Chat IDs as strings. // Send will send a telegram message to a list of chat IDs. message.text is used.
func (t *TelegramDaemon) Send(fromName, fromAddr string, message *Message, address ...string) error { func (t *TelegramDaemon) Send(message *Message, ID ...int64) error {
for _, addr := range address { for _, id := range ID {
ChatID, err := strconv.ParseInt(addr, 10, 64) msg := tg.NewMessage(id, message.Text)
if err != nil { _, err := t.bot.Send(msg)
return err
}
msg := tg.NewMessage(ChatID, message.Text)
_, err = t.bot.Send(msg)
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,7 +12,6 @@ interface formWindow extends Window {
code: string; code: string;
messages: { [key: string]: string }; messages: { [key: string]: string };
confirmation: boolean; confirmation: boolean;
telegramEnabled: boolean;
telegramRequired: boolean; telegramRequired: boolean;
telegramPIN: string; telegramPIN: string;
userExpiryEnabled: boolean; userExpiryEnabled: boolean;

View File

@ -334,7 +334,7 @@ export class accountsList {
this._selectAll.checked = false; this._selectAll.checked = false;
this._modifySettings.classList.add("unfocused"); this._modifySettings.classList.add("unfocused");
this._deleteUser.classList.add("unfocused"); this._deleteUser.classList.add("unfocused");
if (window.emailEnabled) { if (window.emailEnabled || window.telegramEnabled) {
this._announceButton.classList.add("unfocused"); this._announceButton.classList.add("unfocused");
} }
this._extendExpiry.classList.add("unfocused"); this._extendExpiry.classList.add("unfocused");
@ -356,7 +356,7 @@ export class accountsList {
this._modifySettings.classList.remove("unfocused"); this._modifySettings.classList.remove("unfocused");
this._deleteUser.classList.remove("unfocused"); this._deleteUser.classList.remove("unfocused");
this._deleteUser.textContent = window.lang.quantity("deleteUser", list.length); this._deleteUser.textContent = window.lang.quantity("deleteUser", list.length);
if (window.emailEnabled) { if (window.emailEnabled || window.telegramEnabled) {
this._announceButton.classList.remove("unfocused"); this._announceButton.classList.remove("unfocused");
} }
let anyNonExpiries = list.length == 0 ? true : false; let anyNonExpiries = list.length == 0 ? true : false;

View File

@ -18,6 +18,7 @@ declare interface Window {
jfUsers: Array<Object>; jfUsers: Array<Object>;
notificationsEnabled: boolean; notificationsEnabled: boolean;
emailEnabled: boolean; emailEnabled: boolean;
telegramEnabled: boolean;
ombiEnabled: boolean; ombiEnabled: boolean;
usernameEnabled: boolean; usernameEnabled: boolean;
token: string; token: string;

View File

@ -71,9 +71,10 @@ func (app *appContext) checkUsers() {
mode = "delete" mode = "delete"
termPlural = "Deleting" termPlural = "Deleting"
} }
email := false contact := false
if emailEnabled && app.config.Section("user_expiry").Key("send_email").MustBool(true) { if (emailEnabled && app.config.Section("user_expiry").Key("send_email").MustBool(true)) ||
email = true app.config.Section("telegram").Key("enabled").MustBool(false) {
contact = true
} }
// Use a map to speed up checking for deleted users later // Use a map to speed up checking for deleted users later
userExists := map[string]bool{} userExists := map[string]bool{}
@ -114,18 +115,18 @@ func (app *appContext) checkUsers() {
} }
delete(app.storage.users, id) delete(app.storage.users, id)
app.jf.CacheExpiry = time.Now() app.jf.CacheExpiry = time.Now()
if email { if contact {
address, ok := app.storage.emails[id]
if !ok { if !ok {
continue continue
} }
name := app.getAddressOrName(user.ID)
msg, err := app.email.constructUserExpired(app, false) msg, err := app.email.constructUserExpired(app, false)
if err != nil { if err != nil {
app.err.Printf("Failed to construct expiry email for \"%s\": %s", user.Name, err) app.err.Printf("Failed to construct expiry message for \"%s\": %s", user.Name, err)
} else if err := app.email.send(msg, address.(string)); err != nil { } else if err := app.sendByID(msg, user.ID); err != nil {
app.err.Printf("Failed to send expiry email to \"%s\": %s", user.Name, err) app.err.Printf("Failed to send expiry message to \"%s\": %s", name, err)
} else { } else {
app.info.Printf("Sent expiry notification to \"%s\"", address.(string)) app.info.Printf("Sent expiry notification to \"%s\"", name)
} }
} }
} }

View File

@ -116,20 +116,21 @@ func (app *appContext) AdminPage(gc *gin.Context) {
} }
license = string(l) license = string(l)
gcHTML(gc, http.StatusOK, "admin.html", gin.H{ gcHTML(gc, http.StatusOK, "admin.html", gin.H{
"urlBase": app.getURLBase(gc), "urlBase": app.getURLBase(gc),
"cssClass": app.cssClass, "cssClass": app.cssClass,
"contactMessage": "", "contactMessage": "",
"email_enabled": emailEnabled, "email_enabled": emailEnabled,
"notifications": notificationsEnabled, "telegram_enabled": app.config.Section("telegram").Key("enabled").MustBool(false),
"version": version, "notifications": notificationsEnabled,
"commit": commit, "version": version,
"ombiEnabled": ombiEnabled, "commit": commit,
"username": !app.config.Section("email").Key("no_username").MustBool(false), "ombiEnabled": ombiEnabled,
"strings": app.storage.lang.Admin[lang].Strings, "username": !app.config.Section("email").Key("no_username").MustBool(false),
"quantityStrings": app.storage.lang.Admin[lang].QuantityStrings, "strings": app.storage.lang.Admin[lang].Strings,
"language": app.storage.lang.Admin[lang].JSON, "quantityStrings": app.storage.lang.Admin[lang].QuantityStrings,
"langName": lang, "language": app.storage.lang.Admin[lang].JSON,
"license": license, "langName": lang,
"license": license,
}) })
} }