Download src/onboarding.ts from Snapkitty/myapplication: direct link, hf CLI and curl.
- Browser
- Download file 11 kB
-
https://huggingface.co/Snapkitty/myapplication/resolve/main/src/onboarding.ts
- Command line
-
hf download hf://Snapkitty/myapplication/src/onboarding.ts
-
curl -L -o onboarding.ts https://huggingface.co/Snapkitty/myapplication/resolve/main/src/onboarding.ts
11 kB
| import { | |
| Client, GatewayIntentBits, Partials, | |
| EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, | |
| Events, GuildMember, ButtonInteraction, | |
| } from 'discord.js' | |
| import { prisma } from './lib/prisma' | |
| // ββ Role names β create these in your Discord server βββββββββββββββββββββββββ | |
| export const ROLES = { | |
| SOVEREIGN: 'Sovereign', | |
| CONTRIBUTOR: 'Verified Contributor', | |
| DEVELOPER: 'Developer', | |
| BUILDER: 'Builder', | |
| UK_BUILDER: 'UK Builder', | |
| CYPHERPUNK: 'Cypherpunk Guild', | |
| OBSERVER: 'Observer', | |
| } | |
| // ββ GitHub contributor check ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function isRepoContributor(githubUsername: string): Promise<boolean> { | |
| try { | |
| const res = await fetch( | |
| `https://api.github.com/repos/SNAPKITTYWEST/DEVFLOW-FINANCE/contributors?per_page=100`, | |
| { headers: { 'User-Agent': 'snapkitty-bot', ...(process.env.GITHUB_TOKEN ? { Authorization: `token ${process.env.GITHUB_TOKEN}` } : {}) } } | |
| ) | |
| if (!res.ok) return false | |
| const contributors = await res.json() as Array<{ login: string }> | |
| return contributors.some(c => c.login.toLowerCase() === githubUsername.toLowerCase()) | |
| } catch { return false } | |
| } | |
| // ββ Write member to DB ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function upsertCommunityMember(data: { | |
| discordId: string | |
| discordTag: string | |
| role: string | |
| githubUsername?: string | |
| verified?: boolean | |
| }) { | |
| try { | |
| await prisma.communityMember.upsert({ | |
| where: { discordId: data.discordId }, | |
| update: { role: data.role, githubUsername: data.githubUsername, verified: data.verified ?? false, updatedAt: new Date() }, | |
| create: { | |
| discordId: data.discordId, | |
| discordTag: data.discordTag, | |
| role: data.role, | |
| githubUsername: data.githubUsername, | |
| verified: data.verified ?? false, | |
| }, | |
| }) | |
| } catch (e) { console.error('DB write failed:', e) } | |
| } | |
| // ββ Welcome embed + role buttons βββββββββββββββββββββββββββββββββββββββββββββ | |
| function buildWelcomeEmbed(member: GuildMember) { | |
| return new EmbedBuilder() | |
| .setColor(0x6d28d9) | |
| .setTitle('Saint Errant Β· Sovereign Digital Society') | |
| .setDescription( | |
| `**${member.user.username}** β you have been granted entry.\n\n` + | |
| `This is not a server. This is a sovereign digital society.\n` + | |
| `SnapKitty is the operating system. The agents are the mind.\n` + | |
| `The chain is the record. The fortress does not sleep.\n\n` + | |
| `**Declare your guild to receive your clearance:**` | |
| ) | |
| .addFields( | |
| { name: 'β‘ Developer', value: 'Building on the sovereign codebase', inline: true }, | |
| { name: 'π¨ Builder', value: 'Working in the broader ecosystem', inline: true }, | |
| { name: 'π Cypherpunk', value: 'Security, cryptography, zero-trust ops', inline: true }, | |
| { name: 'π¬π§ UK Builder', value: 'UK Treasury Social Nest member', inline: true }, | |
| { name: 'π Observer', value: 'Witness to the machine', inline: true }, | |
| ) | |
| .setFooter({ text: 'Saint Errant Β· Sovereign Digital Society β built in the open, proven in chain' }) | |
| } | |
| function buildRoleButtons() { | |
| const row1 = new ActionRowBuilder<ButtonBuilder>().addComponents( | |
| new ButtonBuilder().setCustomId('role_developer').setLabel('β‘ Developer').setStyle(ButtonStyle.Primary), | |
| new ButtonBuilder().setCustomId('role_builder').setLabel('π¨ Builder').setStyle(ButtonStyle.Secondary), | |
| new ButtonBuilder().setCustomId('role_cypherpunk').setLabel('π Cypherpunk').setStyle(ButtonStyle.Danger), | |
| ) | |
| const row2 = new ActionRowBuilder<ButtonBuilder>().addComponents( | |
| new ButtonBuilder().setCustomId('role_uk').setLabel('π¬π§ UK Builder').setStyle(ButtonStyle.Secondary), | |
| new ButtonBuilder().setCustomId('role_observer').setLabel('π Observer').setStyle(ButtonStyle.Secondary), | |
| ) | |
| return [row1, row2] | |
| } | |
| // ββ Assign role helper ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function assignRole(member: GuildMember, roleName: string): Promise<boolean> { | |
| try { | |
| const role = member.guild.roles.cache.find(r => r.name === roleName) | |
| if (!role) { console.warn(`Role "${roleName}" not found in server`); return false } | |
| await member.roles.add(role) | |
| return true | |
| } catch (e) { console.error('Role assign failed:', e); return false } | |
| } | |
| // ββ Gateway client ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export function startGateway() { | |
| const token = process.env.DISCORD_BOT_TOKEN | |
| if (!token) { console.warn('DISCORD_BOT_TOKEN not set β gateway disabled'); return } | |
| const client = new Client({ | |
| intents: [ | |
| GatewayIntentBits.Guilds, | |
| GatewayIntentBits.GuildMembers, | |
| GatewayIntentBits.GuildMessages, | |
| GatewayIntentBits.MessageContent, | |
| GatewayIntentBits.DirectMessages, | |
| ], | |
| partials: [Partials.Channel, Partials.Message], | |
| }) | |
| // ββ New member joins βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| client.on(Events.GuildMemberAdd, async (member) => { | |
| console.log(`New member: ${member.user.tag}`) | |
| const welcomeChannel = member.guild.channels.cache.find( | |
| ch => ch.name === 'welcome' || ch.name === 'general' | |
| ) | |
| if (welcomeChannel?.isTextBased()) { | |
| await welcomeChannel.send({ | |
| content: `<@${member.id}>`, | |
| embeds: [buildWelcomeEmbed(member)], | |
| components: buildRoleButtons(), | |
| }) | |
| } | |
| // Also DM the member | |
| try { | |
| await member.send({ | |
| embeds: [buildWelcomeEmbed(member)], | |
| components: buildRoleButtons(), | |
| }) | |
| } catch { /* DMs may be closed */ } | |
| }) | |
| // ββ Button interactions ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| client.on(Events.InteractionCreate, async (interaction) => { | |
| if (!interaction.isButton()) return | |
| const btn = interaction as ButtonInteraction | |
| const member = btn.member as GuildMember | |
| if (btn.customId === 'role_developer') { | |
| await assignRole(member, ROLES.DEVELOPER) | |
| await upsertCommunityMember({ discordId: member.id, discordTag: member.user.tag, role: 'developer' }) | |
| await btn.reply({ | |
| content: `**Developer role assigned.**\n\nNow run \`/verify github-username:YourGitHubHandle\` to verify your contributions to DEVFLOW-FINANCE and unlock **Verified Contributor** status.`, | |
| ephemeral: true, | |
| }) | |
| } | |
| else if (btn.customId === 'role_builder') { | |
| await assignRole(member, ROLES.BUILDER) | |
| await upsertCommunityMember({ discordId: member.id, discordTag: member.user.tag, role: 'builder' }) | |
| await btn.reply({ | |
| content: `**Builder role assigned.** Welcome to the ecosystem.\n\nCheck out the War Room at https://collectivekitty.com and use \`/ask\` to talk to any of the 11 agents.`, | |
| ephemeral: true, | |
| }) | |
| } | |
| else if (btn.customId === 'role_uk') { | |
| await assignRole(member, ROLES.UK_BUILDER) | |
| await upsertCommunityMember({ discordId: member.id, discordTag: member.user.tag, role: 'uk_builder' }) | |
| await btn.reply({ | |
| content: `**UK Builder role assigned.**\n\nYou're now part of the UK Treasury Social Nest. Use \`/ask agent:sterling message:walk me through UK Tier 0\` to get started with the UK business credit system.`, | |
| ephemeral: true, | |
| }) | |
| } | |
| else if (btn.customId === 'role_cypherpunk') { | |
| await assignRole(member, ROLES.CYPHERPUNK) | |
| await upsertCommunityMember({ discordId: member.id, discordTag: member.user.tag, role: 'cypherpunk' }) | |
| await btn.reply({ | |
| content: `**Cypherpunk Guild β clearance granted.**\n\n` + | |
| `The fortress runs SHA-256 sealed decisions, WORM immutable ledger, Ed25519 signature verification on every interaction, and a three-pillar preflight that kills unsigned payloads before they're processed.\n\n` + | |
| `Use \`/ask agent:sentinel message:show me the threat model\` to start.\n` + | |
| `The chain is at https://github.com/SNAPKITTYWEST/DEVFLOW-FINANCE`, | |
| ephemeral: true, | |
| }) | |
| } | |
| else if (btn.customId === 'role_observer') { | |
| await assignRole(member, ROLES.OBSERVER) | |
| await upsertCommunityMember({ discordId: member.id, discordTag: member.user.tag, role: 'observer' }) | |
| await btn.reply({ | |
| content: `**Observer clearance granted.**\n\nWitness the machine at https://collectivekitty.com\nThe chain: https://github.com/SNAPKITTYWEST/DEVFLOW-FINANCE`, | |
| ephemeral: true, | |
| }) | |
| } | |
| }) | |
| client.once(Events.ClientReady, (c) => { | |
| console.log(`Gateway online β logged in as ${c.user.tag}`) | |
| console.log(`Watching ${c.guilds.cache.size} server(s)`) | |
| }) | |
| client.login(token) | |
| return client | |
| } | |
| // ββ GitHub verification slash handler (called from index.ts) ββββββββββββββββββ | |
| export async function handleVerify( | |
| discordId: string, | |
| discordTag: string, | |
| githubUsername: string, | |
| member: GuildMember | |
| ): Promise<string> { | |
| const isContributor = await isRepoContributor(githubUsername) | |
| if (isContributor) { | |
| await assignRole(member, ROLES.CONTRIBUTOR) | |
| await upsertCommunityMember({ | |
| discordId, discordTag, | |
| role: 'verified_contributor', | |
| githubUsername, | |
| verified: true, | |
| }) | |
| return `**Verified.** @${githubUsername} is a contributor to DEVFLOW-FINANCE.\n**Verified Contributor** role assigned. Welcome to the core.` | |
| } else { | |
| await assignRole(member, ROLES.DEVELOPER) | |
| await upsertCommunityMember({ | |
| discordId, discordTag, | |
| role: 'developer', | |
| githubUsername, | |
| verified: false, | |
| }) | |
| return `**@${githubUsername}** β not yet a contributor to DEVFLOW-FINANCE.\n**Developer** role assigned. Make your first PR at https://github.com/SNAPKITTYWEST/DEVFLOW-FINANCE and run \`/verify\` again to unlock Verified Contributor.` | |
| } | |
| } | |