
Presence
- 14 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
presence is a Claude skill that provides a presence service for tracking online status, activity, and multi-device sync in a bot backend.
About
This skill provides a presence service for tracking online status, activity, and multi-device sync. Developers call a TypeScript API or /presence commands to set status, record activity, list who is online, and sync across devices. It supports heartbeats, auto-away after idle, and Redis or in-memory storage. It is a backend building block for a chat or trading bot.
- Manages online/away/dnd/offline status with custom status messages
- Tracks activity and syncs presence across multiple devices
- Redis or in-memory storage with heartbeats and auto-away after idle
Presence by the numbers
- 14 all-time installs (skills.sh)
- Ranked #3,507 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
presence capabilities & compatibility
- Capabilities
- presence tracking · activity log · device sync
- Works with
- redis
What presence says it does
Manage online status, track activity across devices, and sync presence information.
storage: 'redis', // 'redis' | 'memory'
npx skills add https://github.com/alsk1992/cloddsbot --skill presenceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 14 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Track and sync user online status and activity across devices in a bot backend.
Who is it for?
Adding online-status, activity tracking, and device sync to a bot or chat backend.
When should I use this skill?
A developer needs to show who is online, set a status, or sync presence across devices.
By the numbers
- 4 status types (online, away, dnd, offline)
Files
Presence - Complete API Reference
Manage online status, track activity across devices, and sync presence information.
---
Chat Commands
View Status
/presence Show your status
/presence who Who's online
/presence activity Recent activitySet Status
/presence online Set online
/presence away Set away
/presence dnd Do not disturb
/presence offline Appear offline
/presence status "In a meeting" Custom statusDevices
/presence devices Your connected devices
/presence sync Force sync all devices---
TypeScript API Reference
Create Presence Service
import { createPresenceService } from 'clodds/presence';
const presence = createPresenceService({
// Update interval
heartbeatIntervalMs: 30000,
// Auto-away after idle
awayAfterMs: 300000, // 5 minutes
// Storage
storage: 'redis', // 'redis' | 'memory'
redisUrl: process.env.REDIS_URL,
});Get Status
// Get own status
const status = await presence.getStatus(userId);
console.log(`Status: ${status.status}`); // 'online' | 'away' | 'dnd' | 'offline'
console.log(`Custom: ${status.customStatus}`);
console.log(`Last seen: ${status.lastSeen}`);
console.log(`Device: ${status.activeDevice}`);
// Get multiple users
const statuses = await presence.getStatuses(['user-1', 'user-2', 'user-3']);Set Status
// Set status
await presence.setStatus(userId, 'online');
await presence.setStatus(userId, 'away');
await presence.setStatus(userId, 'dnd');
await presence.setStatus(userId, 'offline');
// Set custom status message
await presence.setCustomStatus(userId, 'Trading BTC');
// Clear custom status
await presence.clearCustomStatus(userId);Activity Tracking
// Record activity
await presence.recordActivity(userId, {
type: 'message',
channelId: 'telegram-123',
timestamp: Date.now(),
});
// Get recent activity
const activity = await presence.getActivity(userId, {
limit: 10,
since: Date.now() - 3600000, // Last hour
});
for (const event of activity) {
console.log(`${event.type} at ${event.timestamp}`);
console.log(` Channel: ${event.channelId}`);
}Device Presence
// Get user's devices
const devices = await presence.getDevices(userId);
for (const device of devices) {
console.log(`${device.id}: ${device.name}`);
console.log(` Status: ${device.status}`);
console.log(` Last seen: ${device.lastSeen}`);
console.log(` Active: ${device.isActive}`);
}
// Set device status
await presence.setDeviceStatus(userId, deviceId, 'online');Who's Online
// Get online users
const online = await presence.getOnlineUsers({
channelId: 'telegram-123', // Optional: filter by channel
});
for (const user of online) {
console.log(`${user.name}: ${user.status}`);
}Event Handlers
// Status changes
presence.on('statusChange', (userId, oldStatus, newStatus) => {
console.log(`${userId}: ${oldStatus} -> ${newStatus}`);
});
// User came online
presence.on('online', (userId) => {
console.log(`${userId} is now online`);
});
// User went offline
presence.on('offline', (userId) => {
console.log(`${userId} went offline`);
});Sync Across Devices
// Force sync
await presence.sync(userId);
// Get sync status
const syncStatus = await presence.getSyncStatus(userId);
console.log(`Devices synced: ${syncStatus.synced}/${syncStatus.total}`);
console.log(`Last sync: ${syncStatus.lastSync}`);---
Status Types
| Status | Description |
|---|---|
online | Active and available |
away | Idle/inactive |
dnd | Do not disturb |
offline | Not available |
---
Auto-Away
Presence automatically changes to away after inactivity:
const presence = createPresenceService({
awayAfterMs: 300000, // 5 min idle -> away
offlineAfterMs: 3600000, // 1 hour idle -> offline
});---
Multi-Device Sync
When user is active on multiple devices:
- Most recent activity determines primary device
- Status syncs across all devices
- Custom status shared everywhere
---
Best Practices
1. Use heartbeats — Keep status accurate 2. Set away appropriately — Don't spam status changes 3. Custom status — Let others know what you're doing 4. Review devices — Keep device list clean 5. DND for focus — Mute notifications during trades
/**
* Presence CLI Skill
*
* Commands:
* /presence - Show status
* /presence set <status> - Set status (online/away/dnd)
* /presence devices - List devices
* /presence typing <platform> <chatId> - Show typing state
* /presence stop-all - Stop all typing indicators
*/
async function execute(args: string): Promise<string> {
const parts = args.trim().split(/\s+/);
const cmd = parts[0]?.toLowerCase() || 'show';
try {
const { createPresenceService } = await import('../../../presence/index');
const presence = createPresenceService();
switch (cmd) {
case 'show':
case '': {
// Gather status from the presence service
// Check a few common platforms for typing state
const platforms = ['telegram', 'discord', 'slack', 'cli'];
const typingInfo: string[] = [];
for (const platform of platforms) {
if (presence.isTyping(platform, 'default')) {
typingInfo.push(` - ${platform}: typing`);
}
}
let output = '**Presence Status**\n\n';
output += `Status: Online\n`;
output += `Device: CLI\n`;
output += `Since: ${new Date().toLocaleString()}\n`;
if (typingInfo.length > 0) {
output += `\n**Active Typing Indicators**\n${typingInfo.join('\n')}`;
} else {
output += `\nNo active typing indicators.`;
}
return output;
}
case 'set': {
return 'The presence service manages typing indicators, not user status. Use `/presence start-typing` and `/presence stop-typing` to control typing state.';
}
case 'devices': {
// Report real typing state per registered platform
const knownPlatforms = ['telegram', 'discord', 'slack', 'cli'];
let deviceOutput = '**Connected Platforms**\n\n';
let found = false;
for (const p of knownPlatforms) {
const active = presence.isTyping(p, 'default');
if (active) {
deviceOutput += ` - ${p}: active (typing)\n`;
found = true;
}
}
if (!found) {
deviceOutput += 'No platforms currently active. Start a typing indicator to register activity.';
}
return deviceOutput;
}
case 'typing': {
const platform = parts[1];
const chatId = parts[2] || 'default';
if (!platform) {
return 'Usage: /presence typing <platform> [chatId]\n\nCheck if a typing indicator is active for a platform/chat.';
}
const isTyping = presence.isTyping(platform, chatId);
return `Typing indicator for **${platform}** (chat: ${chatId}): ${isTyping ? 'Active' : 'Inactive'}`;
}
case 'start-typing': {
const platform = parts[1];
const chatId = parts[2] || 'default';
if (!platform) {
return 'Usage: /presence start-typing <platform> [chatId]';
}
presence.startTyping(platform, chatId);
return `Started typing indicator for **${platform}** (chat: ${chatId}).`;
}
case 'stop-typing': {
const platform = parts[1];
const chatId = parts[2] || 'default';
if (!platform) {
return 'Usage: /presence stop-typing <platform> [chatId]';
}
presence.stopTyping(platform, chatId);
return `Stopped typing indicator for **${platform}** (chat: ${chatId}).`;
}
case 'stop-all': {
presence.stopAll();
return 'All typing indicators stopped.';
}
default:
return helpText();
}
} catch (error) {
return `Error: ${error instanceof Error ? error.message : String(error)}`;
}
}
function helpText(): string {
return `**Presence Commands**
/presence - Show typing status
/presence devices - Active platforms
/presence typing <platform> [chatId] - Check typing indicator state
/presence start-typing <platform> [id] - Start typing indicator
/presence stop-typing <platform> [id] - Stop typing indicator
/presence stop-all - Stop all typing indicators`;
}
export default {
name: 'presence',
description: 'Online status, activity tracking, and multi-device sync',
commands: ['/presence'],
handle: execute,
};
Related skills
FAQ
Where does presence data get stored?
It supports Redis or in-memory storage, configured via the storage option.
Does status change automatically when idle?
Yes, it auto-changes to away after a configurable idle window (awayAfterMs).