FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
project logo
charmaineDiscord-to-Linear
Discord -> Linear ticket based on emoji reaction trigger
Public
Like
2
Discord-to-Linear
Home
Code
5
backend
3
.vtignore
README.md
deno.json
C
discord-reaction-cron.ts
Branches
1
Pull requests
Remixes
3
History
Environment variables
5
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
5/26/2025
Viewing readonly version of main branch: v121
View latest version
README.md

Discord to Linear Ticket Automation

This automation creates Linear tickets from Discord messages when they receive a specific emoji reaction (🧿). It runs as a cron job every minute to check for new reactions.

šŸš€ Quick Start

  1. Set environment variables: Add all required environment variables to your Val Town environment (see table below)
  2. Configure Discord bot with proper permissions and intents
  3. Set up the cron job to run every minute
  4. Start using: React to Discord messages with 🧿 to create tickets!

šŸ“‹ Environment Variables

Set these in your Val Town environment:

VariableDescriptionRequiredFormat
DISCORD_BOT_TOKENYour Discord bot tokenāœ…String
LINEAR_API_KEYYour Linear API keyāœ…String
LINEAR_TEAM_IDThe Linear team ID where tickets should be createdāœ…UUID format
DISCORD_SERVER_IDYour Discord server IDāœ…Discord ID
DISCORD_MONITORED_CHANNELSComma-separated list of channel IDs to monitorāœ…Channel IDs separated by commas

Example Values:

DISCORD_SERVER_ID=1
DISCORD_MONITORED_CHANNELS=1327384540187983926,1327384540187983927,1327384540187983928
LINEAR_TEAM_ID=6312a2a0-633b-47dc-a225-81d5d1759bf4

šŸ”§ Setup Instructions

1. Create Discord Bot

  1. Go to Discord Developer Portal
  2. Create a new application and bot
  3. Copy the bot token to DISCORD_BOT_TOKEN in your environment variables

2. Configure Discord Bot Permissions

  1. In Discord Developer Portal, go to OAuth2 → URL Generator
  2. Select "bot" scope. Selecting this will unfurl a new Bot Permissions section below
  3. Then select View Channels, Read Message History and Add Reactions
  4. Use the generated URL to invite your bot to your Discord server

3. Enable Required Bot Intents

Critical: Enable Message Content Intent

  1. In Discord Developer Portal, go to "Bot" → "Privileged Gateway Intents"
  2. Enable "Message Content Intent" āœ…

Without this, your bot can see messages exist but cannot read the actual text content of messages.

4. Get Linear API Key

  1. Go to Linear Settings → Security & Access → Personal API Key
  2. Copy it to LINEAR_API_KEY in your environment variables

5. Find Your Linear Team ID

Your team ID must be in UUID format (e.g., 6312a2a0-633b-47dc-a225-81d5d1759bf4).

Method 1: From Linear URL

  • Go to your Linear team
  • Copy the UUID from the URL: https://linear.app/your-team/team/UUID-HERE

Method 2: Use Linear API

  • Make a GraphQL query to get your teams and find the correct UUID

6. Get Discord IDs

To get Discord server and channel IDs:

  1. Enable Developer Mode in Discord (Settings → Advanced → Developer Mode)
  2. Right-click on channel → Copy ID (for DISCORD_MONITORED_CHANNELS)
  3. Right-click on server name → Copy ID (for DISCORD_SERVER_ID)

For multiple channels, separate them with commas:

DISCORD_MONITORED_CHANNELS=1327384540187983926,1327384540187983927

7. Set Up Cron Schedule

  1. Go to your discord-reaction-cron.ts file in Val Town
  2. Set the cron schedule to run every minute: * * * * *

šŸŽÆ How It Works

  1. Cron job runs every minute (discord-reaction-cron.ts)
  2. Connects to Discord using Discord.js SDK
  3. Checks monitored channels for messages with target emoji reactions (🧿)
  4. Creates Linear tickets for new reactions with:
    • Title: [Discord] <actual message content>
    • Description: Full message content + link to Discord message
    • Labels: "discord-import"
    • Priority: Medium (configurable)
  5. Prevents duplicates by tracking processed messages in Val Town blob storage
  6. Cleans up Discord connection after each run

šŸ“ Project Structure

ā”œā”€ā”€ discord-reaction-cron.ts     # Main automation (runs every minute)
ā”œā”€ā”€ backend/
│   ā”œā”€ā”€ config.ts               # Configuration constants and env helpers
│   ā”œā”€ā”€ discord.ts              # Discord.js SDK wrapper
│   └── linear.ts               # Linear SDK wrapper
└── README.md

šŸ”— Key Files

  • discord-reaction-cron.ts - The main automation that runs every minute to check for new reactions
  • backend/config.ts - Configuration constants, environment helpers, and validation
  • backend/discord.ts - Discord.js SDK integration with proper TypeScript types
  • backend/linear.ts - Linear SDK integration for ticket creation

āš™ļø Configuration

Edit /backend/config.ts to customize:

export const CONFIG = { TARGET_EMOJI: "🧿", // Emoji that triggers tickets (unicode character) LINEAR_LABEL_NAME: "discord-import", // Label added to tickets LINEAR_LABEL_COLOR: "#7C3AED", // Color for the discord-import label LINEAR_PRIORITY: 3, // 1=Urgent, 2=High, 3=Medium, 4=Low MAX_TITLE_LENGTH: 100, // Maximum characters in ticket title MAX_MESSAGES_TO_CHECK: 50, // Number of recent messages to check } as const;

šŸŽÆ Emoji Configuration

Standard emojis (🧿, ā¤ļø, šŸ‘): Use the actual unicode character in config Custom Discord emojis (custom_name): Use just the name without colons (ie. charmaine_react instead of :charmaine_react:)

Example: When reacting with 🧿, Discord's API returns "🧿" (not "nazar_amulet"), so use:

TARGET_EMOJI: "🧿" // āœ… Correct TARGET_EMOJI: "nazar_amulet" // āŒ Wrong for unicode emojis

šŸ› ļø Built With

  • Discord REST API (v10) - Direct API calls using fetch for maximum compatibility with Deno/Val Town
  • Linear SDK (v22.0.0) - Official Linear SDK for seamless issue management
  • Val Town Blob Storage - For tracking processed messages and preventing duplicates
  • TypeScript - For type safety and better development experience

šŸ” Troubleshooting

Common Issues

"Required environment variable not set" error:

  • Check that all five environment variables are configured in Val Town
  • Verify there are no extra spaces in the values (they are automatically trimmed)

"teamId must be a UUID" error:

  • Ensure LINEAR_TEAM_ID is in UUID format (e.g., 6312a2a0-633b-47dc-a225-81d5d1759bf4)
  • Don't use the team key (e.g., "VAL") - use the full UUID

"DISCORD_MONITORED_CHANNELS must contain at least one channel ID" error:

  • Ensure the environment variable has at least one valid channel ID
  • For multiple channels, separate with commas: 123,456,789

"Discord API error: 401" or "Discord API error: 403":

  • Verify your DISCORD_BOT_TOKEN is correct and has no extra spaces
  • Ensure the bot has proper permissions in the Discord server
  • Check that the bot is actually added to your Discord server

Tickets created with "Message from username" instead of actual content:

  • Enable "Message Content Intent" in Discord Developer Portal
  • This is the most common issue - the bot can see messages but not read content without this intent

Bot not responding to reactions:

  • Verify the bot is in the Discord server and has correct permissions
  • Check that you're using the exact emoji name configured in TARGET_EMOJI
  • Ensure all channel IDs are correct in DISCORD_MONITORED_CHANNELS
  • The bot doesn't need to be online 24/7 since we use REST API, not Gateway

Cron job not running:

  • Verify the cron schedule is set to * * * * * in Val Town
  • Check Val Town logs for any startup errors

Getting Help

  1. Check Val Town logs for detailed error messages
  2. Verify all environment variables are set correctly
  3. Test with a simple message that has clear text content
  4. Check Discord Developer Portal for bot permissions and intents

šŸ“Š Monitoring

  • Check Val Town logs for cron job execution and any errors
  • Monitor Linear tickets with the "discord-import" label
  • Track processed messages in Val Town blob storage (keys like processed_MESSAGE_ID_nazar_amulet)

šŸŽ‰ Success Tips

  1. Enable Message Content Intent - This is critical for reading message content
  2. Use the correct permissions integer (2147549248) when inviting the bot
  3. Use UUID format for team ID - Not the team key
  4. Test with messages that have actual text content - Empty messages will show as "Message from username"
  5. Monitor the logs - They provide detailed information about what's happening
  6. Set up all environment variables - The automation will validate them on startup

The automation creates tickets with format: [Discord] <actual message content>

React to any Discord message with 🧿 and watch it automatically become a Linear ticket!

Go to top
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Product
FeaturesPricing
Developers
DocsStatusAPI ExamplesNPM Package Examples
Explore
ShowcaseTemplatesNewest ValsTrending ValsNewsletter
Company
AboutBlogCareersBrandhi@val.town
Terms of usePrivacy policyAbuse contact
Ā© 2025 Val Town, Inc.