summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-09-06 20:41:52 +0200
committerTor Andersson <tor@ccxvii.net>2023-09-12 11:11:15 +0200
commita3ae62afd2eecb3831e2483f94d2db6a1d6bb748 (patch)
treeafab182f51b3769feb6c060a48e65513d09c7425
parentcce4df5b31627482ca1ce595223e4ae02800f0dc (diff)
downloadserver-a3ae62afd2eecb3831e2483f94d2db6a1d6bb748.tar.gz
Notification system improvements.
Enable webhook notification support with .env WEBHOOKS=1 Send notifications when invited to a game. Use notification "too soon" logic for webhooks as well. Try sending webhooks 3 times before giving up. Check if player is on the join page instead of the funky "offline" check when sending "your game is ready" notification. Only show mail/webhook notification settings if they are enabled.
-rw-r--r--schema.sql12
-rw-r--r--server.js347
-rw-r--r--views/header.pug3
-rw-r--r--views/index.pug5
-rw-r--r--views/profile.pug33
5 files changed, 198 insertions, 202 deletions
diff --git a/schema.sql b/schema.sql
index ff99076..e121bda 100644
--- a/schema.sql
+++ b/schema.sql
@@ -422,7 +422,6 @@ create view your_turn_reminder as
status = 1
and active in ('All', 'Both', role)
and is_solo = 0
- and notify = 1
and julianday() > julianday(mtime, '+1 hour')
;
@@ -439,6 +438,17 @@ create view your_turn as
and active in ('All', 'Both', role)
;
+drop view if exists invite_reminder;
+create view invite_reminder as
+ select
+ game_id, role, user_id, name, mail, notify
+ from
+ players
+ join users using(user_id)
+ where
+ is_invite = 1
+ ;
+
-- Trigger to remove game data when filing a game as archived
drop trigger if exists trigger_archive_game;
diff --git a/server.js b/server.js
index bae58f6..c98fef8 100644
--- a/server.js
+++ b/server.js
@@ -24,6 +24,12 @@ const SITE_URL = process.env.SITE_URL || "http://" + HTTP_HOST + ":" + HTTP_PORT
const REGEX_MAIL = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
const REGEX_NAME = /^[\p{Alpha}\p{Number}'_-]+( [\p{Alpha}\p{Number}'_-]+)*$/u
+const WEBHOOKS = process.env.WEBHOOKS | 0
+if (WEBHOOKS)
+ console.log("Webhook notifications enabled.")
+else
+ console.log("Webhook notifications disabled.")
+
function LOG_STATS() {
// Count clients connected to join page events
let num_joins = 0
@@ -44,6 +50,12 @@ function LOG_STATS() {
setInterval(LOG_STATS, 60 * 1000)
+/* CONNECTED CLIENT INFO */
+
+var join_clients = {}
+var game_clients = {}
+var game_cookies = {}
+
/*
* Main database.
*/
@@ -90,11 +102,6 @@ if (process.env.MAIL_HOST && process.env.MAIL_PORT && process.env.MAIL_FROM) {
console.log("Mail notifications enabled: ", mailer.options)
} else {
console.log("Mail notifications disabled.")
- mailer = {
- sendMail(obj, callback) {
- callback("DID NOT SEND: " + JSON.stringify(obj,0,4))
- }
- }
}
/*
@@ -151,6 +158,9 @@ function set_static_headers(res, path) {
let app = express()
app.locals.SITE_NAME = SITE_NAME
app.locals.SITE_URL = SITE_URL
+app.locals.ENABLE_MAIL = !!mailer
+app.locals.ENABLE_WEBHOOKS = !!WEBHOOKS
+app.locals.ENABLE_FORUM = process.env.FORUM | 0
app.set('x-powered-by', false)
app.set('etag', false)
app.set('view engine', 'pug')
@@ -275,8 +285,6 @@ const SQL_SELECT_USER_PROFILE = SQL("SELECT * FROM user_profile_view WHERE name=
const SQL_SELECT_USER_DYNAMIC = SQL("select * from user_dynamic_view where user_id=?")
const SQL_SELECT_USER_ID = SQL("SELECT user_id FROM users WHERE name=?").pluck()
-const SQL_OFFLINE_USER = SQL("SELECT * FROM user_view NATURAL JOIN user_last_seen WHERE user_id=? AND julianday() > julianday(atime, ?)")
-
const SQL_SELECT_USER_NOTIFY = SQL("SELECT notify FROM users WHERE user_id=?").pluck()
const SQL_UPDATE_USER_NOTIFY = SQL("UPDATE users SET notify=? WHERE user_id=?")
const SQL_UPDATE_USER_NAME = SQL("UPDATE users SET name=? WHERE user_id=?")
@@ -290,6 +298,7 @@ const SQL_SELECT_WEBHOOK = SQL("SELECT * FROM webhooks WHERE user_id=?")
const SQL_SELECT_WEBHOOK_SEND = SQL("SELECT url, format, prefix FROM webhooks WHERE user_id=? AND error is null")
const SQL_UPDATE_WEBHOOK = SQL("INSERT OR REPLACE INTO webhooks (user_id, url, format, prefix, error) VALUES (?,?,?,?,null)")
const SQL_UPDATE_WEBHOOK_ERROR = SQL("UPDATE webhooks SET error=? WHERE user_id=?")
+const SQL_UPDATE_WEBHOOK_SUCCESS = SQL("UPDATE webhooks SET error=null WHERE user_id=? AND error IS NOT NULL")
const SQL_DELETE_WEBHOOK = SQL("DELETE FROM webhooks WHERE user_id=?")
const SQL_FIND_TOKEN = SQL("SELECT token FROM tokens WHERE user_id=? AND julianday('now') < julianday(time, '+5 minutes')").pluck()
@@ -558,7 +567,7 @@ app.post("/update-webhook", must_be_logged_in, function (req, res) {
SQL_UPDATE_WEBHOOK.run(req.user.user_id, url, format, prefix)
const webhook = SQL_SELECT_WEBHOOK_SEND.get(req.user.user_id)
if (webhook)
- send_webhook(req.user.user_id, webhook, "Test message!")
+ send_webhook(req.user.user_id, webhook, "Test message!", 0)
res.setHeader("refresh", "3; url=/webhook")
res.send("Testing Webhook. Please wait...")
})
@@ -764,8 +773,7 @@ app.post('/message/send', must_be_logged_in, function (req, res) {
})
}
let info = MESSAGE_SEND.run(req.user.user_id, to_user.user_id, subject, body)
- if (to_user.notify)
- mail_new_message(to_user, info.lastInsertRowid, req.user.name)
+ send_notification(to_user, message_link(info.lastInsertRowid), "New message from " + req.user.name)
res.redirect('/inbox')
})
@@ -1440,8 +1448,6 @@ app.get('/rematch/:old_game_id', must_be_logged_in, function (req, res) {
return res.redirect('/join/'+new_game_id)
})
-var join_clients = {}
-
function update_join_clients_deleted(game_id) {
let list = join_clients[game_id]
if (list && list.length > 0) {
@@ -1641,7 +1647,7 @@ app.post('/start/:game_id', must_be_logged_in, function (req, res) {
SQL_UPDATE_GAME_RESULT.run(1, null, game_id)
if (is_solo(players))
SQL_UPDATE_GAME_PRIVATE.run(game_id)
- mail_game_started_notification_to_offline_users(game_id)
+ send_game_started_notification_to_offline_users(game_id)
put_new_state(game_id, state, null, null, ".setup", [seed, game.scenario, options])
@@ -1681,6 +1687,34 @@ app.get('/api/replay/:game_id', function (req, res) {
})
/*
+ * MAIL NOTIFICATIONS
+ */
+
+const MAIL_FROM = process.env.MAIL_FROM || "user@localhost"
+const MAIL_FOOTER = "\n--\nYou can unsubscribe from notifications on your profile page:\n" + SITE_URL + "/profile\n"
+
+function mail_callback(err) {
+ if (err)
+ console.log("MAIL ERROR", err)
+}
+
+function mail_addr(user) {
+ return user.name + " <" + user.mail + ">"
+}
+
+function mail_password_reset_token(user, token) {
+ if (mailer) {
+ let subject = "Password reset request"
+ let body =
+ "Your password reset token is: " + token + "\n\n" +
+ SITE_URL + "/reset-password/" + user.mail + "/" + token + "\n\n" +
+ "If you did not request a password reset you can ignore this mail.\n"
+ console.log("SENT MAIL:", mail_addr(user), subject)
+ mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback)
+ }
+}
+
+/*
* WEBHOOK NOTIFICATIONS
*/
@@ -1702,6 +1736,7 @@ const webhook_text_options = {
function on_webhook_success(user_id) {
console.log("WEBHOOK SENT", user_id)
+ SQL_UPDATE_WEBHOOK_SUCCESS.run(user_id)
}
function on_webhook_error(user_id, error) {
@@ -1709,7 +1744,9 @@ function on_webhook_error(user_id, error) {
SQL_UPDATE_WEBHOOK_ERROR.run(error, user_id)
}
-function send_webhook(user_id, webhook, message) {
+function send_webhook(user_id, webhook, message, retry=2) {
+ if (!WEBHOOKS)
+ return
try {
const text = webhook.prefix + " " + message
const data = webhook.format ? JSON.stringify({ [webhook.format]: text }) : text
@@ -1717,182 +1754,113 @@ function send_webhook(user_id, webhook, message) {
const req = https.request(webhook.url, options, res => {
if (res.statusCode === 200 || res.statusCode === 204)
on_webhook_success(user_id)
- else
- on_webhook_error(user_id, res.statusCode + " " + http.STATUS_CODES[res.statusCode])
+ else {
+ if (retry > 0)
+ retry_webhook(user_id, webhook, message, retry - 1)
+ else
+ on_webhook_error(user_id, res.statusCode + " " + http.STATUS_CODES[res.statusCode])
+ }
})
req.on("timeout", () => {
- on_webhook_error(user_id, "Timeout")
+ if (retry > 0)
+ retry_webhook(user_id, webhook, message, retry - 1)
+ else
+ on_webhook_error(user_id, "Timeout")
req.abort()
})
req.on("error", (err) => {
- on_webhook_error(user_id, err.toString())
+ if (retry > 0)
+ retry_webhook(user_id, webhook, message, retry - 1)
+ else
+ on_webhook_error(user_id, err.toString())
})
req.write(data)
req.end()
} catch (err) {
- on_webhook_error(user_id, err.message)
- }
-}
-
-function webhook_game_link(game, user) {
- if (user.role)
- return SITE_URL + play_url(game.title_id, game.game_id, user.role)
- return SITE_URL + "/join/" + game.game_id
-}
-
-function webhook_game_started(user, game_id) {
- let webhook = SQL_SELECT_WEBHOOK_SEND.get(user.user_id)
- if (webhook) {
- let game = SQL_SELECT_GAME_VIEW.get(game_id)
- let message = webhook_game_link(game, user) + " - Started!"
- send_webhook(user.user_id, webhook, message)
+ if (retry > 0)
+ retry_webhook(user_id, webhook, message, retry - 1)
+ else
+ on_webhook_error(user_id, err.message)
}
}
-function webhook_game_finished(user, game_id) {
- let webhook = SQL_SELECT_WEBHOOK_SEND.get(user.user_id)
- if (webhook) {
- let game = SQL_SELECT_GAME_VIEW.get(game_id)
- let message = webhook_game_link(game, user) + " - Finished!"
- send_webhook(user.user_id, webhook, message)
- }
-}
-
-function webhook_your_turn(user, game_id) {
- let webhook = SQL_SELECT_WEBHOOK_SEND.get(user.user_id)
- if (webhook) {
- let game = SQL_SELECT_GAME_VIEW.get(game_id)
- let message = webhook_game_link(game, user) + " - Your turn!"
- send_webhook(user.user_id, webhook, message)
- }
+function retry_webhook(user_id, webhook, message, retry) {
+ console.log("WEBHOOK RETRY", user_id)
+ setTimeout(() => send_webhook(user_id, webhook, message, retry), 3000 + Math.random() * 7000)
}
/*
- * MAIL NOTIFICATIONS
+ * NOTIFICATIONS
*/
-const MAIL_FROM = process.env.MAIL_FROM || "user@localhost"
-const MAIL_FOOTER = "\n--\nYou can unsubscribe from notifications on your profile page:\n" + SITE_URL + "/profile\n"
-
const SQL_SELECT_NOTIFIED = SQL("SELECT julianday() < julianday(time, ?) FROM last_notified WHERE game_id=? AND user_id=?").pluck()
const SQL_INSERT_NOTIFIED = SQL("INSERT OR REPLACE INTO last_notified (game_id,user_id,time) VALUES (?,?,datetime())")
const SQL_DELETE_NOTIFIED = SQL("DELETE FROM last_notified WHERE game_id=? AND user_id=?")
-const SQL_DELETE_NOTIFIED_ALL = SQL("DELETE FROM last_notified WHERE game_id=?")
-
-const QUERY_LIST_YOUR_TURN = SQL("SELECT * FROM your_turn_reminder")
-function mail_callback(err) {
- if (err)
- console.log("MAIL ERROR", err)
+function delete_last_notified(user, game_id) {
+ SQL_DELETE_NOTIFIED.run(game_id, user.user_id)
}
-function mail_addr(user) {
- return user.name + " <" + user.mail + ">"
+function insert_last_notified(user, game_id) {
+ SQL_INSERT_NOTIFIED.run(game_id, user.user_id)
}
-function mail_game_info(game) {
- let desc = `Game: ${game.title_name}\n`
- desc += `Scenario: ${game.scenario}\n`
- desc += `Players: ${game.player_names}\n`
- desc += "\n"
- if (game.notice && game.notice.length > 0)
- desc += game.notice + "\n"
- return desc + "\n"
+function should_send_reminder(user, game_id) {
+ if (!SQL_SELECT_NOTIFIED.get('+23 hours', game_id, user.user_id))
+ return true
+ return false
}
-function mail_game_link(game, user) {
- return SITE_URL + "/" + game.title_id + "/play.html?game=" + game.game_id + "&role=" + encodeURIComponent(user.role) + "\n"
+function game_play_link(game_id, title_id, user) {
+ return SITE_URL + play_url(title_id, game_id, user.role)
}
-function mail_password_reset_token(user, token) {
- if (mailer) {
- let subject = "Password reset request"
- let body =
- "Your password reset token is: " + token + "\n\n" +
- SITE_URL + "/reset-password/" + user.mail + "/" + token + "\n\n" +
- "If you did not request a password reset you can ignore this mail.\n"
- console.log("SENT MAIL:", mail_addr(user), subject)
- mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback)
- }
+function game_join_link(game_id) {
+ return SITE_URL + "/join/" + game_id
}
-function mail_new_message(user, msg_id, msg_from) {
- if (mailer) {
- let subject = "You have a new message from " + msg_from + "."
- let body =
- "Read the message here:\n" +
- SITE_URL + "/message/read/" + msg_id + "\n" +
- MAIL_FOOTER
- console.log("SENT MAIL:", mail_addr(user), subject)
- mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback)
- }
+function message_link(msg_id) {
+ return SITE_URL + "/message/read/" + msg_id
}
-function mail_game_started_notification(user, game_id) {
- if (mailer) {
- let game = SQL_SELECT_GAME_FULL_VIEW.get(game_id)
- let subject = `${game.title_name} #${game_id} (${user.role}) - Started!`
- let body = mail_game_info(game) +
- "The game has started!\n\n" +
- mail_game_link(game, user) +
- MAIL_FOOTER
- console.log("SENT MAIL:", mail_addr(user), subject)
- mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback)
+function send_notification(user, link, message) {
+ if (WEBHOOKS) {
+ let webhook = SQL_SELECT_WEBHOOK_SEND.get(user.user_id)
+ if (webhook) {
+ console.log("WEBHOOK", user.name, link, message)
+ send_webhook(user.user_id, webhook, link + " - " + message)
+ }
}
-}
-
-function mail_game_finished_notification(user, game_id, result) {
- if (mailer) {
- let game = SQL_SELECT_GAME_FULL_VIEW.get(game_id)
- let subject = `${game.title_name} #${game_id} (${user.role}) - Finished!`
- let body = mail_game_info(game) +
- "Result: " + result + "\n\n" +
- mail_game_link(game, user) +
- MAIL_FOOTER
- console.log("SENT MAIL:", mail_addr(user), subject)
- mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback)
+ if (mailer && user.notify) {
+ console.log("MAIL", mail_addr(user), link, message)
+ mailer.sendMail(
+ {
+ from: MAIL_FROM,
+ to: mail_addr(user),
+ subject: message,
+ text: link + "\n" + MAIL_FOOTER,
+ },
+ mail_callback
+ )
}
}
-function mail_your_turn_notification(user, game_id, interval) {
- if (mailer) {
- let too_soon = SQL_SELECT_NOTIFIED.get(interval, game_id, user.user_id)
- if (!too_soon) {
- SQL_INSERT_NOTIFIED.run(game_id, user.user_id)
- let game = SQL_SELECT_GAME_FULL_VIEW.get(game_id)
- let subject = `${game.title_name} #${game_id} (${user.role}) - Your turn!`
- let body = mail_game_info(game) +
- "It's your turn.\n\n" +
- mail_game_link(game, user) +
- MAIL_FOOTER
- console.log("SENT MAIL:", mail_addr(user), subject)
- mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback)
- }
- }
+function send_join_notification(user, game_id, message) {
+ let title_id = SQL_SELECT_GAME_TITLE.get(game_id)
+ let title_name = TITLES[title_id].title_name
+ send_notification(user, game_join_link(game_id), `${title_name} #${game_id} - ${message}`)
}
-function reset_your_turn_notification(user, game_id) {
- SQL_DELETE_NOTIFIED.run(game_id, user.user_id)
+function send_play_notification(user, game_id, message) {
+ let title_id = SQL_SELECT_GAME_TITLE.get(game_id)
+ let title = TITLES[title_id].title_name
+ send_notification(user, game_play_link(game_id, title_id, user), `${title} #${game_id} (${user.role}) - ${message}`)
}
-function mail_ready_to_start_notification(user, game_id, interval) {
- if (mailer) {
- let too_soon = SQL_SELECT_NOTIFIED.get(interval, game_id, user.user_id)
- if (!too_soon) {
- SQL_INSERT_NOTIFIED.run(game_id, user.user_id)
- let game = SQL_SELECT_GAME_FULL_VIEW.get(game_id)
- let subject = `${game.title_name} #${game_id} - Ready to start!`
- let body = mail_game_info(game) +
- "Your game is ready to start.\n\n" +
- SITE_URL + "/join/" + game_id + "\n" +
- MAIL_FOOTER
- console.log("SENT MAIL:", mail_addr(user), subject)
- mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback)
- }
- }
-}
+const QUERY_LIST_YOUR_TURN = SQL("SELECT * FROM your_turn_reminder")
+const QUERY_LIST_INVITES = SQL("SELECT * FROM invite_reminder")
-function mail_your_turn_notification_to_offline_users(game_id, old_active, active) {
+function send_your_turn_notification_to_offline_users(game_id, old_active, active) {
// Only send notifications when the active player changes.
if (old_active === active)
return
@@ -1902,46 +1870,51 @@ function mail_your_turn_notification_to_offline_users(game_id, old_active, activ
let p_was_active = (old_active === p.role || old_active === 'Both' || old_active === 'All')
let p_is_active = (active === p.role || active === 'Both' || active === 'All')
if (!p_was_active && p_is_active) {
- if (is_player_online(game_id, p.user_id)) {
- if (p.notify)
- reset_your_turn_notification(p, game_id)
+ if (!is_player_online(game_id, p.user_id)) {
+ insert_last_notified(p, game_id)
+ send_play_notification(p, game_id, "Your turn")
} else {
- if (p.notify)
- mail_your_turn_notification(p, game_id, '+15 minutes')
- webhook_your_turn(p, game_id)
+ delete_last_notified(p, game_id)
}
} else {
- if (p.notify)
- reset_your_turn_notification(p, game_id)
+ delete_last_notified(p, game_id)
}
}
}
-function mail_game_started_notification_to_offline_users(game_id) {
+function send_game_started_notification_to_offline_users(game_id) {
let players = SQL_SELECT_PLAYERS.all(game_id)
for (let p of players) {
- if (!is_player_online(game_id, p.user_id)) {
- if (p.notify)
- mail_game_started_notification(p, game_id)
- webhook_game_started(p, game_id)
- }
+ if (!is_player_online(game_id, p.user_id))
+ send_play_notification(p, game_id, "Started")
+ delete_last_notified(p, game_id)
}
}
-function mail_game_finished_notification_to_offline_users(game_id, result) {
+function send_game_finished_notification_to_offline_users(game_id, result) {
let players = SQL_SELECT_PLAYERS.all(game_id)
for (let p of players) {
- if (!is_player_online(game_id, p.user_id)) {
- if (p.notify)
- mail_game_finished_notification(p, game_id, result)
- webhook_game_finished(p, game_id)
- }
+ if (!is_player_online(game_id, p.user_id))
+ send_play_notification(p, game_id, "Finished (" + result + ")")
+ delete_last_notified(p, game_id)
}
}
function notify_your_turn_reminder() {
for (let item of QUERY_LIST_YOUR_TURN.all()) {
- mail_your_turn_notification(item, item.game_id, '+25 hours')
+ if (should_send_reminder(item, item.game_id)) {
+ insert_last_notified(item, item.game_id)
+ send_play_notification(item, item.game_id, "Your turn")
+ }
+ }
+}
+
+function notify_invited_reminder() {
+ for (let item of QUERY_LIST_INVITES.all()) {
+ if (should_send_reminder(item, item.game_id)) {
+ insert_last_notified(item, item.game_id)
+ send_join_notification(item, item.game_id, "You have an invitation")
+ }
}
}
@@ -1949,28 +1922,30 @@ function notify_ready_to_start_reminder() {
for (let game of SQL_SELECT_OPEN_GAMES.all()) {
let players = SQL_SELECT_PLAYERS.all(game.game_id)
if (is_game_ready(game.title_id, game.scenario, game.options, players)) {
- let owner = SQL_OFFLINE_USER.get(game.owner_id, '+3 minutes')
- if (owner) {
- if (owner.notify)
- mail_ready_to_start_notification(owner, game.game_id, '+25 hours')
+ if (!is_player_online(game.game_id, game.owner_id)) {
+ let owner = SQL_SELECT_USER_VIEW.get(game.owner_id)
+ if (should_send_reminder(owner, game.game_id)) {
+ insert_last_notified(owner, game.game_id)
+ send_join_notification(owner, game.game_id, "Ready to start")
+ }
}
}
}
}
-// Check and send daily 'your turn' reminders every 15 minutes.
-setInterval(notify_your_turn_reminder, 15 * 60 * 1000)
+// Send "you've been invited" notifications every 5 minutes.
+setInterval(notify_invited_reminder, 5 * 60 * 1000)
-// Check and send ready to start notifications every 5 minutes.
-setInterval(notify_ready_to_start_reminder, 5 * 60 * 1000)
+// Check and send ready to start notifications every 7 minutes.
+setInterval(notify_ready_to_start_reminder, 7 * 60 * 1000)
+
+// Check and send daily your turn reminders every 17 minutes.
+setInterval(notify_your_turn_reminder, 17 * 60 * 1000)
/*
* GAME SERVER
*/
-var game_clients = {}
-var game_cookies = {}
-
function is_player_online(game_id, user_id) {
if (game_clients[game_id])
for (let other of game_clients[game_id])
@@ -2043,17 +2018,17 @@ function put_snap(game_id, state) {
function put_game_state(game_id, state, old_active) {
// TODO: separate state, undo, and log entries to reuse "snap" json stringifaction?
- if (state.state === "game_over") {
- SQL_UPDATE_GAME_RESULT.run(2, state.result, game_id)
- SQL_DELETE_NOTIFIED_ALL.run(game_id)
- mail_game_finished_notification_to_offline_users(game_id, state.result)
- }
SQL_UPDATE_GAME_STATE.run(game_id, JSON.stringify(state), state.active)
if (game_clients[game_id])
for (let other of game_clients[game_id])
send_state(other, state)
update_join_clients_game(game_id)
- mail_your_turn_notification_to_offline_users(game_id, old_active, state.active)
+ if (state.state === "game_over") {
+ SQL_UPDATE_GAME_RESULT.run(2, state.result, game_id)
+ send_game_finished_notification_to_offline_users(game_id, state.result)
+ } else {
+ send_your_turn_notification_to_offline_users(game_id, old_active, state.active)
+ }
}
function put_new_state(game_id, state, old_active, role, action, args) {
diff --git a/views/header.pug b/views/header.pug
index 61f5250..c417665 100644
--- a/views/header.pug
+++ b/views/header.pug
@@ -4,7 +4,8 @@ header
nav
a(href="/about") About
if user
- a(href="/forum") Forum
+ if ENABLE_FORUM
+ a(href="/forum") Forum
a(href="/games/public") Public
if user.active > 0
a(href="/games/active") Games (#{user.active})
diff --git a/views/index.pug b/views/index.pug
index 6b90638..8e399a7 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -48,3 +48,8 @@ html
a(href="/"+title.title_id)= title.title_name
p!= process.env.SITE_INVITE
+
+ if !ENABLE_MAIL
+ p.error Mail notifications disabled.
+ if !ENABLE_WEBHOOKS
+ p.error Webhook notifications disabled.
diff --git a/views/profile.pug b/views/profile.pug
index 63d3018..d884f8f 100644
--- a/views/profile.pug
+++ b/views/profile.pug
@@ -12,10 +12,14 @@ html
p Welcome, #{user.name}!
p Your mail address is #{user.mail}
- if user.notify
- p <a href="/unsubscribe">Disable mail notifications</a>
- else
- p <a href="/subscribe">Enable mail notifications</a>
+ if ENABLE_MAIL
+ if !user.is_verified
+ p <a href="/verify-mail">Verify your mail address</a>
+
+ if user.notify
+ p <a href="/unsubscribe">Disable mail notifications</a>
+ else
+ p <a href="/subscribe">Enable mail notifications</a>
p
| <a href="/change-password">Change password</a>
@@ -28,16 +32,17 @@ html
br
| <a href="/delete-account">Delete account</a>
- if !user.webhook
- p <a href="/webhook">Configure webhook</a>
- else if user.webhook.error
- dl
- dt <a href="/webhook">Configure webhook</a>
- dd.error ERROR: #{user.webhook.error}
- else
- dl
- dt <a href="/webhook">Configure webhook</a>
- dd= new URL(user.webhook.url).hostname
+ if ENABLE_WEBHOOKS
+ if !user.webhook
+ p <a href="/webhook">Configure webhook</a>
+ else if user.webhook.error
+ dl
+ dt <a href="/webhook">Configure webhook</a>
+ dd.error ERROR: #{user.webhook.error}
+ else
+ dl
+ dt <a href="/webhook">Configure webhook</a>
+ dd= new URL(user.webhook.url).hostname
p
form(action="/logout" method="post")