
Auto Reply
- 62 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
Auto-Reply is a Claude Code skill that creates rules to automatically respond to chat messages by pattern, keyword, and condition, with cooldowns and dynamic responses.
About
Auto-Reply is a skill that creates rules for automatic responses based on patterns, keywords, and conditions. It matches messages by keyword, exact, regex, startsWith, or endsWith patterns, gates replies by time, day, user, channel, or role, applies cooldowns to prevent spam, and supports dynamic variable and API-backed responses. A developer uses it to automate chat responses in a bot.
- Auto-responds to messages by keyword, exact, regex, prefix, or suffix patterns
- Applies conditions (time window, day, user, channel, role) and per-user/channel cooldowns
- Supports dynamic responses with variables and API-backed replies
Auto Reply by the numbers
- 62 all-time installs (skills.sh)
- Ranked #984 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
auto-reply capabilities & compatibility
- Capabilities
- auto reply · message matching · cooldown control
- Use cases
- orchestration
- Runs
- Runs locally
What auto-reply says it does
Create rules for automatic responses based on patterns, keywords, and conditions.
/autoreply test <message> Test which rules match
npx skills add https://github.com/alsk1992/cloddsbot --skill auto-replyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 62 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Auto-respond to chat messages using pattern rules with conditions and cooldowns.
Who is it for?
automating repetitive chat replies in a bot
Skip if: one-to-one human support or free-form conversational AI responses
When should I use this skill?
you want a bot to auto-respond to messages matching a pattern
What you get
Pattern-based auto-reply rules that fire under configured conditions with anti-spam cooldowns.
By the numbers
- 5 pattern types
- 6 condition types
Files
Auto-Reply - Complete API Reference
Create rules for automatic responses based on patterns, keywords, and conditions.
---
Chat Commands
List Rules
/autoreply list List all rules
/autoreply active Show active rules only
/autoreply stats Rule statisticsCreate Rules
/autoreply add <pattern> <response> Simple keyword match
/autoreply add-regex <regex> <response> Regex pattern
/autoreply add-keywords <kw1,kw2> <resp> Keyword ruleManage Rules
/autoreply enable <id> Enable rule
/autoreply disable <id> Disable rule
/autoreply remove <id> Remove rule
/autoreply edit <id> <new-response> Update response
/autoreply get <id> Rule detailsTesting
/autoreply test <message> Test which rules match
/autoreply simulate <message> Preview responseAdvanced
/autoreply cooldown <id> <seconds> Set cooldown
/autoreply schedule <id> <start-end> Active hours (e.g. 9-17)
/autoreply priority <id> <number> Set priority (higher first)
/autoreply channel <id> <channel> Restrict to channel
/autoreply clear-cooldowns Clear all cooldowns
/autoreply reload Reload rules from disk---
TypeScript API Reference
Create Auto-Reply Manager
import { createAutoReplyManager } from 'clodds/auto-reply';
const autoReply = createAutoReplyManager({
// Storage
storage: 'sqlite',
dbPath: './auto-reply.db',
// Defaults
defaultCooldownMs: 0,
defaultPriority: 0,
// Limits
maxRulesPerUser: 100,
maxResponseLength: 2000,
});Add Simple Rule
// Keyword match
await autoReply.addRule({
name: 'greeting',
pattern: {
type: 'keyword',
value: 'hello',
caseSensitive: false,
},
response: 'Hi there! How can I help?',
});Add Regex Rule
// Regex pattern
await autoReply.addRule({
name: 'price-query',
pattern: {
type: 'regex',
value: /price\s+(btc|eth|sol)/i,
},
response: async (match, ctx) => {
const symbol = match[1].toUpperCase();
const price = await getPrice(symbol);
return `${symbol} price: $${price}`;
},
});Add Conditional Rule
// With conditions
await autoReply.addRule({
name: 'trading-hours',
pattern: {
type: 'keyword',
value: 'trade',
},
conditions: [
// Only during market hours
{
type: 'time',
start: '09:30',
end: '16:00',
timezone: 'America/New_York',
},
// Only on weekdays
{
type: 'day',
days: ['mon', 'tue', 'wed', 'thu', 'fri'],
},
// Only for certain users
{
type: 'user',
userIds: ['user-123', 'user-456'],
},
],
response: 'Markets are open! What would you like to trade?',
elseResponse: 'Markets are closed. Try again during trading hours.',
});Add Cooldown
// Prevent spam
await autoReply.addRule({
name: 'faq',
pattern: {
type: 'keyword',
value: 'faq',
},
response: 'Check our FAQ at https://...',
cooldown: {
perUser: 60000, // 60s per user
perChannel: 10000, // 10s per channel
global: 5000, // 5s global
},
});Dynamic Responses
// Response with variables
await autoReply.addRule({
name: 'welcome',
pattern: {
type: 'exact',
value: '!welcome',
},
response: 'Welcome {{user.name}}! You joined {{user.joinDate}}.',
variables: {
'user.name': (ctx) => ctx.user.displayName,
'user.joinDate': (ctx) => ctx.user.createdAt.toDateString(),
},
});
// Response with API call
await autoReply.addRule({
name: 'portfolio',
pattern: {
type: 'keyword',
value: 'portfolio',
},
response: async (match, ctx) => {
const portfolio = await getPortfolio(ctx.user.id);
return `Your portfolio: $${portfolio.totalValue.toFixed(2)}`;
},
});List Rules
const rules = await autoReply.listRules();
for (const rule of rules) {
console.log(`${rule.id}: ${rule.name}`);
console.log(` Pattern: ${rule.pattern.value}`);
console.log(` Enabled: ${rule.enabled}`);
console.log(` Triggers: ${rule.triggerCount}`);
}Test Rule
// Test which rules would match
const matches = await autoReply.test('hello world', {
userId: 'user-123',
channelId: 'telegram-456',
});
for (const match of matches) {
console.log(`Rule: ${match.rule.name}`);
console.log(`Response: ${match.response}`);
}Enable/Disable
await autoReply.enable('rule-id');
await autoReply.disable('rule-id');Delete Rule
await autoReply.deleteRule('rule-id');---
Pattern Types
| Type | Example | Description |
|---|---|---|
keyword | hello | Contains keyword |
exact | !help | Exact match only |
regex | /price\s+\w+/i | Regular expression |
startsWith | ! | Starts with prefix |
endsWith | ? | Ends with suffix |
---
Condition Types
| Type | Description |
|---|---|
time | Active during time window |
day | Active on specific days |
user | Only for specific users |
channel | Only in specific channels |
role | Only for users with role |
custom | Custom function |
---
Response Variables
| Variable | Description |
|---|---|
{{user.name}} | User display name |
{{user.id}} | User ID |
{{channel.name}} | Channel name |
{{match[0]}} | Full regex match |
{{match[1]}} | First capture group |
{{date}} | Current date |
{{time}} | Current time |
---
Best Practices
1. Use priorities — Important rules first 2. Set cooldowns — Prevent spam 3. Test patterns — Verify before enabling 4. Use conditions — Context-aware responses 5. Monitor triggers — Check rule effectiveness
/**
* Auto-Reply CLI Skill
*
* Commands:
* /autoreply list - List all rules
* /autoreply add <pattern> <response> - Add rule
* /autoreply remove <id> - Remove rule
* /autoreply enable <id> - Enable rule
* /autoreply disable <id> - Disable rule
*/
async function execute(args: string): Promise<string> {
const parts = args.trim().split(/\s+/);
const cmd = parts[0]?.toLowerCase() || 'help';
try {
const { createAutoReplyService } = await import('../../../auto-reply/index');
const service = createAutoReplyService();
switch (cmd) {
case 'list':
case 'ls': {
const rules = service.listRules();
if (!rules.length) return 'No auto-reply rules configured. Use `/autoreply add` to create one.';
let output = `**Auto-Reply Rules** (${rules.length})\n\n`;
for (const rule of rules) {
output += `[${rule.id}] ${rule.name} (priority: ${rule.priority})\n`;
output += ` Enabled: ${rule.enabled ? 'yes' : 'no'}\n`;
output += ` Conditions: ${rule.conditions.map(c => `${c.type}:${c.pattern || c.keywords?.join(',') || ''}`).join(', ')}\n`;
if (rule.description) output += ` Description: ${rule.description}\n`;
if (rule.cooldownMs) output += ` Cooldown: ${rule.cooldownMs / 1000}s\n`;
output += '\n';
}
return output;
}
case 'add': {
const pattern = parts[1];
const response = parts.slice(2).join(' ');
if (!pattern || !response) return 'Usage: /autoreply add <pattern> <response>';
const id = `rule-${Date.now()}`;
service.addRule({
id,
name: `Rule: ${pattern}`,
enabled: true,
priority: 0,
conditions: [{ type: 'contains', pattern, ignoreCase: true }],
response: { type: 'text', content: response },
});
service.save();
return `Auto-reply rule added: id=${id}, pattern="${pattern}", response="${response}"`;
}
case 'add-regex': {
const regex = parts[1];
const response = parts.slice(2).join(' ');
if (!regex || !response) return 'Usage: /autoreply add-regex <regex-pattern> <response>';
const id = `rule-${Date.now()}`;
service.addRule({
id,
name: `Regex: ${regex}`,
enabled: true,
priority: 0,
conditions: [{ type: 'regex', pattern: regex, ignoreCase: true }],
response: { type: 'text', content: response },
});
service.save();
return `Auto-reply regex rule added: id=${id}, pattern=/${regex}/i`;
}
case 'add-keywords': {
const keywords = parts[1]?.split(',');
const response = parts.slice(2).join(' ');
if (!keywords?.length || !response) return 'Usage: /autoreply add-keywords <kw1,kw2,...> <response>';
const id = `rule-${Date.now()}`;
service.addRule({
id,
name: `Keywords: ${keywords.join(',')}`,
enabled: true,
priority: 0,
conditions: [{ type: 'keywords', keywords, ignoreCase: true, minKeywords: 1 }],
response: { type: 'text', content: response },
});
service.save();
return `Auto-reply keyword rule added: id=${id}, keywords=[${keywords.join(', ')}]`;
}
case 'remove':
case 'delete': {
if (!parts[1]) return 'Usage: /autoreply remove <id>';
const removed = service.removeRule(parts[1]);
if (removed) {
service.save();
return `Auto-reply rule \`${parts[1]}\` removed.`;
}
return `Rule \`${parts[1]}\` not found.`;
}
case 'enable': {
if (!parts[1]) return 'Usage: /autoreply enable <id>';
const enabled = service.enableRule(parts[1]);
if (enabled) {
service.save();
return `Auto-reply rule \`${parts[1]}\` enabled.`;
}
return `Rule \`${parts[1]}\` not found.`;
}
case 'disable': {
if (!parts[1]) return 'Usage: /autoreply disable <id>';
const disabled = service.disableRule(parts[1]);
if (disabled) {
service.save();
return `Auto-reply rule \`${parts[1]}\` disabled.`;
}
return `Rule \`${parts[1]}\` not found.`;
}
case 'get':
case 'info': {
if (!parts[1]) return 'Usage: /autoreply get <id>';
const rule = service.getRule(parts[1]);
if (!rule) return `Rule \`${parts[1]}\` not found.`;
let output = `**Rule: ${rule.name}** (${rule.id})\n\n`;
output += `Enabled: ${rule.enabled ? 'yes' : 'no'}\n`;
output += `Priority: ${rule.priority}\n`;
output += `Conditions:\n`;
for (const c of rule.conditions) {
output += ` - ${c.type}: ${c.pattern || c.keywords?.join(', ') || '(all)'}\n`;
}
output += `Response: ${rule.response.content}\n`;
if (rule.cooldownMs) output += `Cooldown: ${rule.cooldownMs / 1000}s${rule.perUserCooldown ? ' (per-user)' : ''}\n`;
if (rule.timeWindow) output += `Time window: ${rule.timeWindow.startHour}:00 - ${rule.timeWindow.endHour}:00\n`;
if (rule.channels?.length) output += `Channels: ${rule.channels.join(', ')}\n`;
return output;
}
case 'active': {
const rules = service.listRules().filter(r => r.enabled);
if (!rules.length) return 'No active auto-reply rules.';
let output = `**Active Auto-Reply Rules** (${rules.length})\n\n`;
for (const rule of rules) {
output += `[${rule.id}] ${rule.name} (priority: ${rule.priority})\n`;
output += ` Conditions: ${rule.conditions.map(c => `${c.type}:${c.pattern || c.keywords?.join(',') || ''}`).join(', ')}\n`;
if (rule.cooldownMs) output += ` Cooldown: ${rule.cooldownMs / 1000}s\n`;
if (rule.timeWindow) output += ` Schedule: ${rule.timeWindow.startHour}:00 - ${rule.timeWindow.endHour}:00\n`;
if (rule.channels?.length) output += ` Channels: ${rule.channels.join(', ')}\n`;
output += '\n';
}
return output;
}
case 'stats': {
const rules = service.listRules();
const active = rules.filter(r => r.enabled).length;
const disabled = rules.filter(r => !r.enabled).length;
const withCooldown = rules.filter(r => r.cooldownMs && r.cooldownMs > 0).length;
const withSchedule = rules.filter(r => r.timeWindow).length;
const withChannels = rules.filter(r => r.channels && r.channels.length > 0).length;
let output = '**Auto-Reply Statistics**\n\n';
output += `Total rules: ${rules.length}\n`;
output += `Active: ${active}\n`;
output += `Disabled: ${disabled}\n`;
output += `With cooldown: ${withCooldown}\n`;
output += `With schedule: ${withSchedule}\n`;
output += `With channel filter: ${withChannels}\n\n`;
if (rules.length > 0) {
output += '**Rules by Type:**\n';
const typeCounts: Record<string, number> = {};
for (const rule of rules) {
for (const c of rule.conditions) {
typeCounts[c.type] = (typeCounts[c.type] || 0) + 1;
}
}
for (const [type, count] of Object.entries(typeCounts)) {
output += ` ${type}: ${count}\n`;
}
}
return output;
}
case 'edit': {
const ruleId = parts[1];
const newResponse = parts.slice(2).join(' ');
if (!ruleId || !newResponse) return 'Usage: /autoreply edit <id> <new-response>';
const updated = service.updateRule(ruleId, {
response: { type: 'text', content: newResponse },
});
if (updated) {
service.save();
return `Rule \`${ruleId}\` response updated to: "${newResponse}"`;
}
return `Rule \`${ruleId}\` not found.`;
}
case 'test':
case 'simulate': {
const testMsg = parts.slice(1).join(' ');
if (!testMsg) return 'Usage: /autoreply test <message>';
const rules = service.listRules().filter(r => r.enabled);
const matches: string[] = [];
for (const rule of rules) {
// Manually check each rule condition against the test message
let allMatched = true;
for (const condition of rule.conditions) {
const text = condition.ignoreCase ? testMsg.toLowerCase() : testMsg;
const pattern = condition.pattern
? (condition.ignoreCase ? condition.pattern.toLowerCase() : condition.pattern)
: '';
let matched = false;
switch (condition.type) {
case 'exact': matched = text === pattern; break;
case 'contains': matched = text.includes(pattern); break;
case 'startsWith': matched = text.startsWith(pattern); break;
case 'endsWith': matched = text.endsWith(pattern); break;
case 'regex': {
try {
const pat = condition.pattern || '';
if (pat.length > 200) { matched = false; break; }
const flags = condition.ignoreCase ? 'i' : '';
matched = new RegExp(pat, flags).test(testMsg.slice(0, 10000));
} catch { matched = false; }
break;
}
case 'keywords': {
const kws = condition.keywords || [];
const min = condition.minKeywords || 1;
const found = kws.filter(kw => text.includes(condition.ignoreCase ? kw.toLowerCase() : kw));
matched = found.length >= min;
break;
}
}
if (!matched) { allMatched = false; break; }
}
if (allMatched) {
matches.push(` [${rule.id}] ${rule.name} -> "${rule.response.content}"`);
}
}
if (matches.length === 0) return `No rules match message: "${testMsg}"`;
return `**Rules matching** "${testMsg}":\n\n${matches.join('\n')}`;
}
case 'cooldown': {
const ruleId = parts[1];
const seconds = parseInt(parts[2], 10);
if (!ruleId || isNaN(seconds)) return 'Usage: /autoreply cooldown <id> <seconds>';
const updated = service.updateRule(ruleId, { cooldownMs: seconds * 1000 });
if (updated) {
service.save();
return `Rule \`${ruleId}\` cooldown set to ${seconds}s.`;
}
return `Rule \`${ruleId}\` not found.`;
}
case 'schedule': {
const ruleId = parts[1];
const timeRange = parts[2];
if (!ruleId || !timeRange) return 'Usage: /autoreply schedule <id> <start-end> (e.g. 9-17)';
const match = timeRange.match(/^(\d{1,2})-(\d{1,2})$/);
if (!match) return 'Invalid time range. Use format: 9-17 (start hour - end hour, 24h)';
const startHour = parseInt(match[1], 10);
const endHour = parseInt(match[2], 10);
if (startHour < 0 || startHour > 23 || endHour < 0 || endHour > 23) {
return 'Hours must be 0-23.';
}
const updated = service.updateRule(ruleId, {
timeWindow: { startHour, endHour },
});
if (updated) {
service.save();
return `Rule \`${ruleId}\` schedule set to ${startHour}:00 - ${endHour}:00.`;
}
return `Rule \`${ruleId}\` not found.`;
}
case 'priority': {
const ruleId = parts[1];
const priority = parseInt(parts[2], 10);
if (!ruleId || isNaN(priority)) return 'Usage: /autoreply priority <id> <number>';
const updated = service.updateRule(ruleId, { priority });
if (updated) {
service.save();
return `Rule \`${ruleId}\` priority set to ${priority}.`;
}
return `Rule \`${ruleId}\` not found.`;
}
case 'channel': {
const ruleId = parts[1];
const channel = parts[2];
if (!ruleId || !channel) return 'Usage: /autoreply channel <id> <channel>';
const rule = service.getRule(ruleId);
if (!rule) return `Rule \`${ruleId}\` not found.`;
const channels = rule.channels || [];
if (!channels.includes(channel)) channels.push(channel);
const updated = service.updateRule(ruleId, { channels });
if (updated) {
service.save();
return `Rule \`${ruleId}\` restricted to channels: ${channels.join(', ')}`;
}
return `Rule \`${ruleId}\` not found.`;
}
case 'clear-cooldowns': {
service.clearCooldowns();
return 'All auto-reply cooldowns cleared.';
}
case 'reload': {
service.load();
const rules = service.listRules();
return `Reloaded ${rules.length} auto-reply rules from disk.`;
}
default:
return helpText();
}
} catch (error) {
return `Error: ${error instanceof Error ? error.message : String(error)}`;
}
}
function helpText(): string {
return `**Auto-Reply Commands**
/autoreply list - List all rules
/autoreply active - Show active rules only
/autoreply stats - Rule statistics
/autoreply add <pattern> <response> - Add contains-match rule
/autoreply add-regex <regex> <response> - Add regex rule
/autoreply add-keywords <kw1,kw2> <response> - Add keyword rule
/autoreply remove <id> - Remove a rule
/autoreply enable <id> - Enable a rule
/autoreply disable <id> - Disable a rule
/autoreply edit <id> <new-response> - Update rule response
/autoreply get <id> - Rule details
/autoreply test <message> - Test which rules match
/autoreply cooldown <id> <seconds> - Set cooldown
/autoreply schedule <id> <start-end> - Set active hours (e.g. 9-17)
/autoreply priority <id> <number> - Set rule priority
/autoreply channel <id> <channel> - Restrict to channel
/autoreply clear-cooldowns - Clear all cooldowns
/autoreply reload - Reload rules from disk
Rules saved to ~/.clodds/auto-reply-rules.json`;
}
export default {
name: 'auto-reply',
description: 'Automatic response rules, patterns, and scheduled messages',
commands: ['/autoreply', '/ar'],
handle: execute,
};
Related skills
FAQ
What pattern types are supported?
keyword, exact, regex, startsWith, and endsWith.
Can replies be limited to certain times or users?
Yes, via conditions for time window, day, user, channel, and role.