Context-aware wellness coach — custom reminders, tip progress, and adjustable check-ins.
window.ggbro. This is the complete plugin.
{
"id": "com.ggbro.plugin.session-coach",
"name": "Session Coach",
"version": "3.0.0",
"author": "ggbro",
"description": "Context-aware wellness coach — custom reminders, tip progress, and adjustable check-ins.",
"main": "plugin.js",
"styles": "styles.css",
"permissions": ["ui.toast", "ui.panel", "ui.sound", "storage", "app.context"]
}
/**
* Session Coach v3 — context-aware tips, custom notes, progress.
*/
(async function () {
var started = Date.now();
var tipsShown = 0;
var autoCheckins = true;
var intervalMin = 60;
var checkTimer = null;
var lastTip = '';
var customNote = '';
var ctxLabel = '—';
var tips = [
'Tip: pin busy servers and mute noisy channels for a cleaner sidebar.',
'Tip: Streamer Mode hides personal info when you’re live.',
'Tip: Use search to hop channels fast.',
'Tip: Favorite GIFs live in the GIF picker for one-click reactions.',
'Tip: YouTube Playlists can share a room queue with friends.',
'Tip: Plugins stay sandboxed — only install packs you trust.',
'Tip: Take a sip of water. Hydration is a power move.',
'Tip: Stretch your shoulders for 20 seconds — future you says thanks.',
'Tip: The 20-20-20 rule — every 20 min, look 20 ft away for 20 sec.',
];
try {
tipsShown = parseInt((await ggbro.storage.get('tipsShown')).value || '0', 10) || 0;
autoCheckins = ((await ggbro.storage.get('auto')).value || '1') !== '0';
var iv = parseInt((await ggbro.storage.get('intervalMin')).value || '60', 10);
if (iv >= 15 && iv <= 180) intervalMin = iv;
customNote = (await ggbro.storage.get('customNote')).value || '';
lastTip = (await ggbro.storage.get('lastTip')).value || '';
} catch (e) {
ggbro.log('storage', e && e.message);
}
async function refreshContext() {
try {
var ctx = await ggbro.getContext();
if (!ctx || !ctx.view) {
ctxLabel = 'unknown';
return;
}
if (ctx.view === 'channel') ctxLabel = 'In a channel' + (ctx.serverId ? ' · server' : '');
else if (ctx.view === 'dm') ctxLabel = 'In a DM';
else if (ctx.view === 'group-dm') ctxLabel = 'In a group DM';
else if (ctx.view === 'friends') ctxLabel = 'Friends / home';
else ctxLabel = String(ctx.view);
} catch (e) {
ctxLabel = 'context off';
}
}
function pickTip() {
return tips[Math.floor(Math.random() * tips.length)];
}
function formatUptime(ms) {
var mins = Math.floor(ms / 60000);
if (mins < 60) return mins + ' min';
var hrs = Math.floor(mins / 60);
var rem = mins % 60;
return hrs + 'h ' + rem + 'm';
}
async function paint() {
await refreshContext();
await ggbro.ui.registerPanel({
id: 'coach',
title: 'Session Coach',
schema: [
{
type: 'notice',
id: 'where',
text: 'You’re here: ' + ctxLabel + ' · session ' + formatUptime(Date.now() - started),
tone: 'info',
},
{ type: 'section', id: 's0', title: 'Session' },
{ type: 'text', id: 'up', label: 'Time in app', value: formatUptime(Date.now() - started) },
{ type: 'progress', id: 'tipsBar', label: 'Tips shown (session goals)', value: Math.min(tipsShown, 12), max: 12 },
{ type: 'text', id: 'shown', label: 'Tips shown', value: String(tipsShown) },
{ type: 'text', id: 'last', label: 'Last tip', value: lastTip || '(none yet)' },
{ type: 'section', id: 's1', title: 'Coach' },
{
type: 'textarea',
id: 'note',
label: 'Personal reminder (optional)',
value: customNote,
placeholder: 'e.g. Stand up every hour…',
rows: 2,
},
{ type: 'button', id: 'tip', label: 'Give me a tip' },
{ type: 'button', id: 'checkin', label: 'Check in now' },
{ type: 'toggle', id: 'auto', label: 'Automatic check-ins', value: autoCheckins },
{
type: 'slider',
id: 'interval',
label: 'Check-in interval (minutes)',
value: intervalMin,
min: 15,
max: 180,
step: 15,
},
],
});
}
function bumpTip(msg) {
tipsShown += 1;
lastTip = String(msg || '').slice(0, 200);
ggbro.storage.set('tipsShown', String(tipsShown)).catch(function () {});
ggbro.storage.set('lastTip', lastTip).catch(function () {});
}
function showTip(extra) {
var body = pickTip();
if (customNote) body = customNote + ' · ' + body;
var msg = (extra ? extra + ' ' : '') + body;
bumpTip(msg);
ggbro.playSound({ id: 'friendRequest' }).catch(function () {});
ggbro.toast({ message: msg.slice(0, 200), type: 'info' });
paint().catch(function () {});
}
function armAuto() {
if (checkTimer) clearInterval(checkTimer);
checkTimer = null;
if (!autoCheckins) return;
checkTimer = setInterval(function () {
showTip('Session check-in — you’ve been here ' + formatUptime(Date.now() - started) + '.');
}, intervalMin * 60 * 1000);
}
await paint();
showTip();
armAuto();
setInterval(function () {
paint().catch(function () {});
}, 60000);
ggbro.onEvent(function (event, payload) {
if (event === 'context') {
paint().catch(function () {});
return;
}
if (event !== 'panelAction' || !payload) return;
if (payload.fieldId === 'tip') {
showTip();
return;
}
if (payload.fieldId === 'checkin') {
showTip('Manual check-in — uptime ' + formatUptime(Date.now() - started) + '.');
return;
}
if (payload.fieldId === 'note') {
customNote = String(payload.value || '').slice(0, 200);
ggbro.storage.set('customNote', customNote).catch(function () {});
paint();
ggbro.toast({ message: customNote ? 'Reminder saved' : 'Reminder cleared', type: 'success' });
return;
}
if (payload.fieldId === 'auto') {
autoCheckins = !!payload.value;
ggbro.storage.set('auto', autoCheckins ? '1' : '0').catch(function () {});
armAuto();
ggbro.toast({
message: autoCheckins ? 'Auto check-ins on' : 'Auto check-ins off',
type: 'info',
});
return;
}
if (payload.fieldId === 'interval') {
intervalMin = Math.max(15, Math.min(180, Number(payload.value) || 60));
ggbro.storage.set('intervalMin', String(intervalMin)).catch(function () {});
armAuto();
paint();
ggbro.toast({ message: 'Interval set to ' + intervalMin + ' min', type: 'info' });
}
});
})();
:root {
--ggbro-plugin-session-coach: 1;
}
# Session Coach (v2)
Wellness panel: tip button, check-in now, auto check-in toggle, and interval select (30 / 60 / 120 min).