Discord roles based on reputation.
Sites like StackOverflow and Discourse pioneered reputation systems where users would be automatically given enhanced privileges as a result of ongoing peer recognition. In order to enhance the Dojo Discord community and encourage mutual support among Dojo developers, we propose to introduce a similar type of reputation-based permissioning system, implemented through an open-source Discord bot called Daimyō (a Japanese feudal lord).
- Encourage Quality Contributions: Reward helpful Discord members with recognition and elevated status
- Peer Recognition: Use community feedback (:dojo: reactions) as the basis for advancement
- Dynamic Hierarchy: Maintain an adaptive role system that reflects current activity levels
- Legitimize Roles: Create a trusted reputation system that can be used for other processes (judging, governance, etc.)
The Daimyō bot manages three Discord roles that form a progression path:
- Default role: All Discord members receive this upon joining
- Requirements: None (automatic)
- Privileges: Base member status
- Permanent: This role is never removed once assigned (gates channel access)
- Requirements: Accumulate 50 :dojo: reactions from at least CEIL(10% of total Senpai+Sensei count) unique users with Senpai or Sensei roles
- Privileges: Can help promote others to Senpai, elevated community status
- Requirements: Accumulate 30 :dojo: reactions from at least CEIL(20% of total Sensei count) unique Sensei
- Privileges: Can help promote others to Sensei, highest community status
- Decay Rule: Must maintain 30+ Sensei reactions within the last 360 days or be demoted to Senpai (no diversity requirement for decay)
- Felt Role: Members with the Felt role are automatically granted Sensei and are exempt from decay (core team)
| Role | Can Promote to Senpai | Can Promote to Sensei | Automatic on Join | Subject to Decay |
|---|---|---|---|---|
| Kōhai | No | No | Yes | No |
| Senpai | Yes | No | No | No |
| Sensei | Yes | Yes | No | Yes (360 days) |
The bot tracks reactions using the :dojo: emoji (custom server emoji). When a user reacts to a message with :dojo:, the following is recorded:
- Message ID: The message being reacted to
- Message Author: The user receiving recognition
- Reactor: The user giving the reaction
- Reactor Role: The role of the reactor at the time of reaction (Kōhai, Senpai, or Sensei)
- Timestamp: When the reaction occurred
- Self-Reactions Don't Count: Users cannot react to their own messages
- One Reaction Per User Per Message: Only the first :dojo: reaction from each user counts
- Role Matters: Reactions from Senpai/Sensei count toward Senpai promotion; only Sensei reactions count toward Sensei promotion
- Diversity Requirements: Promotions require reactions from a minimum percentage of unique users in the qualifying role(s)
- Kōhai Reactions: Reactions from Kōhai are tracked and displayed in stats but do not count toward promotion
- Snapshot-Based: Reactor role at time of reaction is preserved (demotions don't retroactively invalidate past reactions)
A user is promoted from Kōhai to Senpai when:
- They accumulate 50 or more :dojo: reactions from users with Senpai or Sensei roles
- These reactions must come from at least CEIL(10% of total Senpai+Sensei) unique users
Calculation Method:
valid_reactions = reactions where reactor_role IN [Senpai, Sensei]
unique_reactors = count_unique(valid_reactions.reactor_id)
total_reactors = count(discord_members where role IN [Senpai, Sensei])
required_unique = ceil(total_reactors * 0.10)
if count(valid_reactions) >= 50 AND unique_reactors >= required_unique:
promote_to_senpai()
Examples:
- 10 Senpai+Sensei total → need 50 reactions from ≥1 unique user
- 30 Senpai+Sensei total → need 50 reactions from ≥3 unique users
A user is promoted from Senpai to Sensei when:
- They accumulate 30 or more :dojo: reactions from users with Sensei role
- These reactions must come from at least CEIL(20% of total Sensei count) unique Sensei
Calculation Method:
valid_reactions = reactions where reactor_role IN [Sensei]
unique_reactors = count_unique(valid_reactions.reactor_id)
total_reactors = count(discord_members where role == Sensei)
required_unique = ceil(total_reactors * 0.20)
if count(valid_reactions) >= 30 AND unique_reactors >= required_unique:
promote_to_sensei()
Examples:
- 5 Sensei total → need 30 reactions from ≥1 unique Sensei
- 20 Sensei total → need 30 reactions from ≥4 unique Sensei
Bootstrap Note: Initial Sensei roles will be manually assigned by admins using the /daimyo-set-role command.
A Sensei is demoted to Senpai if their time-windowed Sensei score falls below 30 within a 360-day rolling window.
Calculation Method:
recent_sensei_reactions = reactions where
reactor_role == Sensei AND
timestamp >= (now - 360 days)
if count(recent_sensei_reactions) < 30:
demote_to_senpai()
Decay Check Frequency: Run daily at midnight UTC
- Reactor Demotion: When a Sensei is demoted to Senpai, their past reactions still count as Sensei reactions (snapshot at time of reaction)
- Reactor Removal: If a user leaves the server, their reactions remain in the database and continue to count
- Multiple Promotions: A user could theoretically go from Kōhai → Senpai → Sensei in quick succession if they have enough qualifying reactions
Display reputation statistics for a user (defaults to command invoker)
Response includes:
- Current role
- Total :dojo: reactions received
- Breakdown by reactor role (Kōhai, Senpai, Sensei)
- Progress to next role
- For Sensei: time-windowed score
Example Output (Kōhai → Senpai):
🎌 Reputation Stats for @username
Current Role: Kōhai
Total :dojo: reactions: 38
- From Kōhai: 15 (display only)
- From Senpai: 18
- From Sensei: 5
Progress to Senpai: 23/50 reactions (27 more needed) | 8/10 unique reactors ✓
(Requires 50 reactions from 10 unique Senpai/Sensei - currently 10% of 100 total)
Example Output (Senpai → Sensei):
🎌 Reputation Stats for @username
Current Role: Senpai
Total :dojo: reactions: 147
- From Kōhai: 32 (display only)
- From Senpai: 45
- From Sensei: 70
Progress to Sensei: 70/30 reactions ✓ | 5/8 unique Sensei (3 more needed)
(Requires 30 reactions from 8 unique Sensei - currently 20% of 40 Sensei)
Example Output (Sensei with decay status):
🎌 Reputation Stats for @username
Current Role: Sensei
Total :dojo: reactions (all-time): 312
- From Kōhai: 89 (display only)
- From Senpai: 134
- From Sensei: 89
Sensei reactions (last 360 days): 42/30 ✓
Display top users by reputation, optionally filtered by role
Arguments:
role(optional): Filter to show only users with a specific role
Example Output:
🏆 Daimyō Leaderboard (Sensei)
1. @user1 - 245 Sensei reactions
2. @user2 - 198 Sensei reactions
3. @user3 - 167 Sensei reactions
...
Manually trigger a full sync of roles based on current reaction counts. Recalculates all scores, applies promotions and demotions, and returns summary of changes.
Display detailed audit information for a user's reputation data. Shows all reactions received with timestamps and reactor info, score calculations, and role history.
The database stores only historical event data. All current state (roles, members, join dates) is queried from Discord.
reactions {
id: uuid (primary key)
message_id: string
message_author_id: string // Discord user ID
reactor_id: string // Discord user ID
reactor_role_at_time: enum(Kohai, Senpai, Sensei)
timestamp: timestamp
unique(message_id, reactor_id) // one reaction per user per message
}
role_history {
id: uuid (primary key)
user_id: string // Discord user ID
role: enum(Kohai, Senpai, Sensei)
reason: string // "promotion", "demotion", "decay"
timestamp: timestamp
}
SQLite is used for storing historical event data. File-based, no external dependencies, and sufficient for a single-server bot. Easy to backup and version control.
PostgreSQL or similar would be needed for multi-server scaling in the future.
- Language: TypeScript
- Framework: Discord.js v14
- Database: SQLite with better-sqlite3
- Database Access: Raw SQL
- Scheduling: node-cron (for daily decay checks)
- Testing: Jest
- Linting: ESLint + Prettier
daimyo/
├── backend/
│ ├── src/
│ │ ├── index.ts # Entry point, client initialization
│ │ ├── events/
│ │ │ ├── ready.ts # Bot ready event
│ │ │ ├── messageReactionAdd.ts # Track :dojo: reactions
│ │ │ └── guildMemberAdd.ts # Auto-assign Kohai role
│ │ ├── commands/
│ │ │ ├── stats.ts # /stats command
│ │ │ ├── leaderboard.ts # /leaderboard command
│ │ │ ├── sync.ts # /daimyo-sync command
│ │ │ └── audit.ts # /daimyo-audit command
│ │ ├── services/
│ │ │ ├── database.ts # Database connection and queries
│ │ │ ├── reputation.ts # Reputation calculation logic
│ │ │ ├── roleManager.ts # Discord role assignment
│ │ │ └── decay.ts # Decay calculation and enforcement
│ │ ├── jobs/
│ │ │ └── decayCheck.ts # Daily cron job for decay
│ │ ├── utils/
│ │ │ ├── config.ts # Configuration management
│ │ │ └── logger.ts # Logging utility
│ │ └── types.ts # TypeScript types
│ ├── package.json
│ ├── tsconfig.json
│ └── .env.example
├── spec/
│ ├── REPUTATION.md # This document
│ └── BLOCKCHAIN.md
└── README.md
- messageReactionAdd: Validate reactions, store in database, check for promotion
- guildMemberAdd: Assign Kōhai role to new members
- Reactions are tracked in all server channels
- Calculate Senpai and Sensei scores for users
- Apply promotion/demotion logic
- Interface with Role Manager to update Discord roles
- Send DM to user when promoted or demoted
- Promotion checks triggered on each reaction and during daily cron job
- Assign/remove Discord roles via API
- Query current roles via Discord.js member cache
- Handle errors gracefully (missing permissions, etc.)
- Discord roles are the source of truth for user status
- Runs daily at midnight UTC using node-cron
- Checks all users for promotion/demotion eligibility
- Identifies Sensei users with insufficient recent reactions
- Triggers promotions/demotions through Reputation Service
Discord as Source of Truth: Current user roles are NOT stored in the database. Discord roles are authoritative.
Role Lookups:
The bot uses Discord.js's built-in member cache for role queries.
With the GuildMembers intent enabled, Discord.js maintains a local cache of all members and their roles.
Role lookups are in-memory and require no API calls.
On Startup:
The bot fetches all guild members on the ready event to populate the cache.
After startup, the cache auto-updates via gateway events.
Historical Data:
The reactions table preserves reactor_role_at_time as a snapshot for historical accuracy.
The role_history table provides an audit trail of role changes.
Environment variables required:
DISCORD_BOT_TOKEN=<your_bot_token>
DISCORD_GUILD_ID=<dojo_discord_server_id>
DOJO_EMOJI_NAME=dojo
DOJO_EMOJI_ID=<custom_emoji_id> # if custom emoji
KOHAI_ROLE_ID=<role_id>
SENPAI_ROLE_ID=<role_id>
SENSEI_ROLE_ID=<role_id>
DATABASE_PATH=./daimyo.db
DECAY_WINDOW_DAYS=360
SENPAI_REACTION_THRESHOLD=50
SENPAI_UNIQUE_PERCENT=0.10
SENSEI_REACTION_THRESHOLD=30
SENSEI_UNIQUE_PERCENT=0.20
Guilds- Access to guild infoGuildMembers- Track member joins and maintain member/role cacheGuildMessageReactions- Core functionalityGuildMessages- Context for reactions (may not be needed)
- Read Messages/View Channels
- Manage Roles
- Use Slash Commands
- Add Reactions (for testing/verification)
- Slash commands must be registered with Discord before the bot starts using the Discord REST API
- Commands can be registered globally or per-guild (recommend per-guild for development, global for production)
- Percentage-Based Scaling: Add upper bounds or more sophisticated scaling for diversity requirements as community grows very large
- Badges/Achievements: Award special badges for milestones (e.g., "First Sensei", "100 Reactions in a Month")
- Reaction Velocity: Track and display trending contributors
- Category-Based Reputation: Track reactions by channel category (e.g., "Cairo Expert", "Networking Guru")
- Kōhai Contribution Badges: Special cosmetic badges for Kōhai who give many helpful reactions
- Multi-Server Support: Allow Daimyō to run on multiple Discord servers
- Web Dashboard: Display leaderboards and stats on a public website
- Integration with Other Systems: Use reputation scores in Game Jam judging, governance voting weights, etc.
- Nomination System: Allow Sensei to nominate worthy members for expedited promotion
- Appeals Process: Allow users to appeal demotions or request manual review
This specification is a living document and will be updated as the project evolves.