diff --git a/api.go b/api.go
index 24868b8..cb02894 100644
--- a/api.go
+++ b/api.go
@@ -826,18 +826,44 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
invite.UserMinutes = req.UserMinutes
}
invite.ValidTill = validTill
- if emailEnabled && req.Email != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
- app.debug.Printf("%s: Sending invite email", inviteCode)
- invite.Email = req.Email
- msg, err := app.email.constructInvite(inviteCode, invite, app, false)
- if err != nil {
- invite.Email = fmt.Sprintf("Failed to send to %s", req.Email)
- app.err.Printf("%s: Failed to construct invite email: %v", inviteCode, err)
- } else if err := app.email.send(msg, req.Email); err != nil {
- invite.Email = fmt.Sprintf("Failed to send to %s", req.Email)
- app.err.Printf("%s: %s: %v", inviteCode, invite.Email, err)
- } else {
- app.info.Printf("%s: Sent invite email to \"%s\"", inviteCode, req.Email)
+ if req.SendTo != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
+ addressValid := false
+ discord := ""
+ app.debug.Printf("%s: Sending invite message", inviteCode)
+ if discordEnabled && !strings.Contains(req.SendTo, "@") {
+ users := app.discord.GetUsers(req.SendTo)
+ if len(users) == 0 {
+ invite.SendTo = fmt.Sprintf("Failed: User not found: \"%s\"", req.SendTo)
+ } else if len(users) > 1 {
+ invite.SendTo = fmt.Sprintf("Failed: Multiple users found: \"%s\"", req.SendTo)
+ } else {
+ invite.SendTo = req.SendTo
+ addressValid = true
+ discord = users[0].User.ID
+ }
+ } else if emailEnabled {
+ addressValid = true
+ invite.SendTo = req.SendTo
+ }
+ if addressValid {
+ msg, err := app.email.constructInvite(inviteCode, invite, app, false)
+ if err != nil {
+ invite.SendTo = fmt.Sprintf("Failed to send to %s", req.SendTo)
+ app.err.Printf("%s: Failed to construct invite message: %v", inviteCode, err)
+ } else {
+ var err error
+ if discord != "" {
+ err = app.discord.SendDM(msg, discord)
+ } else {
+ err = app.email.send(msg, req.SendTo)
+ }
+ if err != nil {
+ invite.SendTo = fmt.Sprintf("Failed to send to %s", req.SendTo)
+ app.err.Printf("%s: %s: %v", inviteCode, invite.SendTo, err)
+ } else {
+ app.info.Printf("%s: Sent invite email to \"%s\"", inviteCode, req.SendTo)
+ }
+ }
}
}
if req.Profile != "" {
@@ -901,8 +927,8 @@ func (app *appContext) GetInvites(gc *gin.Context) {
if inv.RemainingUses != 0 {
invite.RemainingUses = inv.RemainingUses
}
- if inv.Email != "" {
- invite.Email = inv.Email
+ if inv.SendTo != "" {
+ invite.SendTo = inv.SendTo
}
if len(inv.Notify) != 0 {
var address string
diff --git a/discord.go b/discord.go
index 9129ffb..63ecf6d 100644
--- a/discord.go
+++ b/discord.go
@@ -111,13 +111,13 @@ func (d *DiscordDaemon) GetUsers(username string) []*dg.Member {
hasDiscriminator := strings.Contains(username, "#")
var users []*dg.Member
for _, member := range members {
- if !hasDiscriminator {
- userSplit := strings.Split(member.User.Username, "#")
- if strings.Contains(userSplit[0], username) {
- users = append(users, member)
+ if hasDiscriminator {
+ if member.User.Username+"#"+member.User.Discriminator == username {
+ return []*dg.Member{member}
}
- } else if strings.Contains(member.User.Username, username) {
- return nil
+ }
+ if strings.Contains(member.User.Username, username) {
+ users = append(users, member)
}
}
return users
@@ -283,6 +283,18 @@ func (d *DiscordDaemon) commandPIN(s *dg.Session, m *dg.MessageCreate, sects []s
d.tokens = d.tokens[:len(d.tokens)-1]
}
+func (d *DiscordDaemon) SendDM(message *Message, userID ...string) error {
+ channels := make([]string, len(userID))
+ for i, id := range userID {
+ channel, err := d.bot.UserChannelCreate(id)
+ if err != nil {
+ return err
+ }
+ channels[i] = channel.ID
+ }
+ return d.Send(message, channels...)
+}
+
func (d *DiscordDaemon) Send(message *Message, channelID ...string) error {
msg := ""
var embeds []*dg.MessageEmbed
diff --git a/html/admin.html b/html/admin.html
index 31d5db6..31db2b2 100644
--- a/html/admin.html
+++ b/html/admin.html
@@ -501,7 +501,14 @@
+ {{ if .discord_enabled }}
+
+
+
+
+ {{ else }}
+ {{ end }}
diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json
index a308a99..3936b8c 100644
--- a/lang/admin/en-us.json
+++ b/lang/admin/en-us.json
@@ -97,7 +97,8 @@
"notifyInviteExpiry": "On expiry",
"notifyUserCreation": "On user creation",
"sendPIN": "Ask the user to send the PIN below to the bot.",
- "searchDiscordUser": "Start typing the Discord username to link it."
+ "searchDiscordUser": "Start typing the Discord username to find the user.",
+ "findDiscordUser": "Find Discord user"
},
"notifications": {
"changedEmailAddress": "Changed email address of {n}.",
diff --git a/models.go b/models.go
index 3641eb1..890bb62 100644
--- a/models.go
+++ b/models.go
@@ -50,7 +50,7 @@ type generateInviteDTO struct {
UserDays int `json:"user-days,omitempty" example:"1"` // Number of days till user expiry
UserHours int `json:"user-hours,omitempty" example:"2"` // Number of hours till user expiry
UserMinutes int `json:"user-minutes,omitempty" example:"3"` // Number of minutes till user expiry
- Email string `json:"email" example:"jeff@jellyf.in"` // Send invite to this address
+ SendTo string `json:"send-to" example:"jeff@jellyf.in"` // Send invite to this address or discord name
MultipleUses bool `json:"multiple-uses" example:"true"` // Allow multiple uses
NoLimit bool `json:"no-limit" example:"false"` // No invite use limit
RemainingUses int `json:"remaining-uses" example:"5"` // Remaining invite uses
@@ -100,7 +100,7 @@ type inviteDTO struct {
UsedBy map[string]int64 `json:"used-by,omitempty"` // Users who have used this invite mapped to their creation time in Epoch/Unix time
NoLimit bool `json:"no-limit,omitempty"` // If true, invite can be used any number of times
RemainingUses int `json:"remaining-uses,omitempty"` // Remaining number of uses (if applicable)
- Email string `json:"email,omitempty"` // Email the invite was sent to (if applicable)
+ SendTo string `json:"send_to,omitempty"` // Email/Discord username the invite was sent to (if applicable)
NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not
NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not
Label string `json:"label,omitempty" example:"For Friends"` // Optional label for the invite
diff --git a/storage.go b/storage.go
index a190961..bd1b569 100644
--- a/storage.go
+++ b/storage.go
@@ -95,7 +95,7 @@ type Invite struct {
UserDays int `json:"user-days,omitempty"`
UserHours int `json:"user-hours,omitempty"`
UserMinutes int `json:"user-minutes,omitempty"`
- Email string `json:"email"`
+ SendTo string `json:"email"`
// Used to be stored as formatted time, now as Unix.
UsedBy [][]string `json:"used-by"`
Notify map[string]map[string]bool `json:"notify"`
diff --git a/ts/modules/invites.ts b/ts/modules/invites.ts
index ea7fff3..e43cd50 100644
--- a/ts/modules/invites.ts
+++ b/ts/modules/invites.ts
@@ -1,5 +1,5 @@
import { _get, _post, _delete, toClipboard, toggleLoader, toDateString } from "../modules/common.js";
-import { newDiscordSearch } from "../modules/discord.js";
+import { DiscordUser, newDiscordSearch } from "../modules/discord.js";
class DOMInvite implements Invite {
updateNotify = (checkbox: HTMLInputElement) => {
@@ -26,6 +26,7 @@ class DOMInvite implements Invite {
document.dispatchEvent(inviteDeletedEvent);
}
})
+
private _label: string = "";
get label(): string { return this._label; }
set label(label: string) {
@@ -83,10 +84,10 @@ class DOMInvite implements Invite {
this._middle.querySelector("strong.inv-remaining").textContent = remaining;
}
- private _email: string = "";
- get email(): string { return this._email };
- set email(address: string) {
- this._email = address;
+ private _send_to: string = "";
+ get send_to(): string { return this._send_to };
+ set send_to(address: string) {
+ this._send_to = address;
const container = this._infoArea.querySelector(".tooltip") as HTMLDivElement;
const icon = container.querySelector("i");
const chip = container.querySelector("span.inv-email-chip");
@@ -101,7 +102,7 @@ class DOMInvite implements Invite {
} else {
container.classList.add("mr-1");
chip.classList.add("chip");
- if (address.includes("Failed to send to")) {
+ if (address.includes("Failed")) {
icon.classList.remove("ri-mail-line");
icon.classList.add("ri-mail-close-line");
chip.classList.remove("~neutral");
@@ -373,7 +374,7 @@ class DOMInvite implements Invite {
update = (invite: Invite) => {
this.code = invite.code;
this.created = invite.created;
- this.email = invite.email;
+ this.send_to = invite.send_to;
this.expiresIn = invite.expiresIn;
if (window.notificationsEnabled) {
this.notifyCreation = invite.notifyCreation;
@@ -483,7 +484,7 @@ export class inviteList implements inviteList {
function parseInvite(invite: { [f: string]: string | number | { [name: string]: number } | boolean }): Invite {
let parsed: Invite = {};
parsed.code = invite["code"] as string;
- parsed.email = invite["email"] as string || "";
+ parsed.send_to = invite["send_to"] as string || "";
parsed.label = invite["label"] as string || "";
let time = "";
let userExpiryTime = "";
@@ -521,6 +522,7 @@ function parseInvite(invite: { [f: string]: string | number | { [name: string]:
export class createInvite {
private _sendToEnabled = document.getElementById("create-send-to-enabled") as HTMLInputElement;
private _sendTo = document.getElementById("create-send-to") as HTMLInputElement;
+ private _discordSearch: HTMLSpanElement;
private _userExpiryToggle = document.getElementById("create-user-expiry-enabled") as HTMLInputElement;
private _uses = document.getElementById('create-uses') as HTMLInputElement;
private _infUses = document.getElementById("create-inf-uses") as HTMLInputElement;
@@ -543,6 +545,8 @@ export class createInvite {
private _invDuration = document.getElementById('inv-duration');
private _userExpiry = document.getElementById('user-expiry');
+ private _sendToDiscord: (passData: string) => void;
+
// Broadcast when new invite created
private _newInviteEvent = new CustomEvent("newInviteEvent");
private _firstLoad = true;
@@ -577,9 +581,19 @@ export class createInvite {
if (state) {
this._sendToEnabled.parentElement.classList.remove("~neutral");
this._sendToEnabled.parentElement.classList.add("~urge");
+ if (window.discordEnabled) {
+ this._discordSearch.classList.remove("~neutral");
+ this._discordSearch.classList.add("~urge");
+ this._discordSearch.onclick = () => this._sendToDiscord("");
+ }
} else {
this._sendToEnabled.parentElement.classList.remove("~urge");
this._sendToEnabled.parentElement.classList.add("~neutral");
+ if (window.discordEnabled) {
+ this._discordSearch.classList.remove("~urge");
+ this._discordSearch.classList.add("~neutral");
+ this._discordSearch.onclick = null;
+ }
}
}
@@ -733,7 +747,7 @@ export class createInvite {
"multiple-uses": (this.uses > 1 || this.infiniteUses),
"no-limit": this.infiniteUses,
"remaining-uses": this.uses,
- "email": this.sendToEnabled ? this.sendTo : "",
+ "send-to": this.sendToEnabled ? this.sendTo : "",
"profile": this.profile,
"label": this.label
};
@@ -762,7 +776,6 @@ export class createInvite {
this._userDays.disabled = true;
this._userHours.disabled = true;
this._userMinutes.disabled = true;
- this.sendToEnabled = false;
this._createButton.onclick = this.create;
this.sendTo = "";
this.uses = 1;
@@ -799,11 +812,22 @@ export class createInvite {
this._minutes.onchange = this._checkDurationValidity;
document.addEventListener("profileLoadEvent", () => { this.loadProfiles(); }, false);
- if (!window.emailEnabled) {
+ if (!window.emailEnabled && !window.discordEnabled) {
document.getElementById("create-send-to-container").classList.add("unfocused");
}
+
+ if (window.discordEnabled) {
+ this._discordSearch = document.getElementById("create-send-to-search") as HTMLSpanElement;
+ this._sendToDiscord = newDiscordSearch(
+ window.lang.strings("findDiscordUser"),
+ window.lang.strings("searchDiscordUser"),
+ window.lang.strings("select"),
+ (user: DiscordUser) => {
+ this.sendTo = user.name;
+ window.modals.discord.close();
+ }
+ );
+ }
+ this.sendToEnabled = false;
}
}
-
-
-
diff --git a/ts/typings/d.ts b/ts/typings/d.ts
index ba40de0..eb5be55 100644
--- a/ts/typings/d.ts
+++ b/ts/typings/d.ts
@@ -109,7 +109,7 @@ interface Invite {
code?: string;
expiresIn?: string;
remainingUses?: string;
- email?: string;
+ send_to?: string;
usedBy?: { [name: string]: number };
created?: number;
notifyExpiry?: boolean;
diff --git a/views.go b/views.go
index 241dd47..b083391 100644
--- a/views.go
+++ b/views.go
@@ -258,8 +258,8 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
}
return
}
- email := app.storage.invites[code].Email
- if strings.Contains(email, "Failed") {
+ email := app.storage.invites[code].SendTo
+ if strings.Contains(email, "Failed") || !strings.Contains(email, "@") {
email = ""
}
data := gin.H{