This guide will show you how to create a Node.js application that forwards messages from the Jotihunt Telegram bot to Discord or WhatsApp. This can help teams stay updated on Jotihunt events through their preferred communication channels.

What You Can Forward

With this forwarder, you can:

  • Forward Jotihunt Telegram messages to Discord channels
  • Forward messages to WhatsApp (using unofficial WhatsApp Web API)
  • Preserve message formatting (bold, links, etc.)
  • Handle different types of updates (hints, assignments, news)

Prerequisites

Before you begin, ensure you have:

  1. Node.js installed (version 18.x or higher)
  2. A Telegram account and API credentials
  3. Discord bot token and channel ID (optional, for Discord forwarding)
  4. WhatsApp group name (optional, for WhatsApp forwarding)

Setting Up Your Telegram Forwarder

1

Create a new Node.js project

First, create a new directory and initialize your project:

mkdir jotihunt-forwarder
cd jotihunt-forwarder
npm init -y

Install the required dependencies:

npm install telegram discord.js whatsapp-web.js dotenv qrcode-terminal
2

Set up environment variables

Create a .env file in your project root. Here’s how to get each required value:

Telegram API Credentials:

  1. Visit my.telegram.org/apps
  2. Log in with your phone number
  3. Create a new application if you don’t have one
  4. Copy the api_id and api_hash values

Discord Bot Setup:

  1. Go to the Discord Developer Portal
  2. Click “New Application” and name it
  3. Go to the “Bot” section and create a bot
  4. Copy the bot token
  5. Enable “Message Content Intent” under Privileged Gateway Intents
  6. To get the channel ID, enable Developer Mode in Discord (Settings > App Settings > Advanced)
  7. Right-click the target channel and click “Copy ID”

WhatsApp Group Setup:

  1. Note the exact name of your WhatsApp group (case sensitive)
  2. You must be a member of the group you want to forward messages to
  3. Make sure to keep your WhatsApp connected on your phone while using the forwarder

Add these values to your .env file:

.env
# Telegram API credentials
TELEGRAM_API_ID=123456                      # From my.telegram.org/apps
TELEGRAM_API_HASH=abcdef123456abcdef12      # From my.telegram.org/apps
TELEGRAM_STRING_SESSION=                    # Leave empty, will be generated on first run
TELEGRAM_CHAT_USERNAME=@Jotihunt_bot        # The Jotihunt bot username (@Jotihunt_bot)

# Discord Bot credentials (optional)
DISCORD_TOKEN=your_discord_bot_token        # From Discord Developer Portal
DISCORD_CHANNEL_ID=123456789012345678       # Right-click channel > Copy ID

# WhatsApp Configuration (optional)
WHATSAPP_GROUP_NAME=My Group Name           # Exact group name, case sensitive

Never commit your .env file to version control. Make sure to add .env to your .gitignore.

3

Create the forwarder code

Create a new file called index.js in your project root and add the following code:

index.js
require('dotenv').config();

const { TelegramClient } = require('telegram');
const { StringSession } = require('telegram/sessions');
const { NewMessage } = require('telegram/events');
const { Client: DiscordClient, GatewayIntentBits } = require('discord.js');
const { Client: WhatsAppClient, LocalAuth } = require('whatsapp-web.js');
const qrcode = require('qrcode-terminal');

// Load env variables
const {
TELEGRAM_API_ID,
TELEGRAM_API_HASH,
TELEGRAM_STRING_SESSION,
TELEGRAM_CHAT_USERNAME,
DISCORD_TOKEN,
DISCORD_CHANNEL_ID,
WHATSAPP_GROUP_NAME
} = process.env;

if (!TELEGRAM_API_ID || !TELEGRAM_API_HASH || !TELEGRAM_CHAT_USERNAME) {
console.error('Missing Telegram configuration in .env');
process.exit(1);
}

// ----------------------- TELEGRAM -----------------------
const telegramClient = new TelegramClient(
new StringSession(TELEGRAM_STRING_SESSION || ''),
parseInt(TELEGRAM_API_ID, 10),
TELEGRAM_API_HASH,
{ connectionRetries: 5 }
);

// ----------------------- DISCORD -----------------------
let discordClient;
if (DISCORD_TOKEN && DISCORD_CHANNEL_ID) {
discordClient = new DiscordClient({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages]
});

discordClient.once('ready', () => {
console.log(`Discord: Logged in as ${discordClient.user.tag}`);
});

discordClient.login(DISCORD_TOKEN).catch(err =>
console.error('Discord login failed:', err)
);
}

// ----------------------- WHATSAPP -----------------------
let whatsappClient;
let whatsappGroupId = null;

if (WHATSAPP_GROUP_NAME) {
whatsappClient = new WhatsAppClient({
authStrategy: new LocalAuth(),
puppeteer: { headless: true }
});

whatsappClient.on('qr', qr => {
console.log('Scan this QR code to log into WhatsApp:');
qrcode.generate(qr, { small: true });
});

whatsappClient.on('ready', async () => {
console.log('WhatsApp: Client is ready');

try {
  const chats = await whatsappClient.getChats();
  const group = chats.find(
    chat => chat.isGroup && chat.name === WHATSAPP_GROUP_NAME
  );

  if (!group) {
    console.error(`WhatsApp: Group "${WHATSAPP_GROUP_NAME}" not found.`);
  } else {
    whatsappGroupId = group.id._serialized;
    console.log(`WhatsApp: Found group "${group.name}" with ID ${whatsappGroupId}`);
  }
} catch (err) {
  console.error('WhatsApp group lookup failed:', err);
}
});

whatsappClient.initialize();
}

// ----------------------- MESSAGE HANDLING -----------------------
function formatMessage(message) {
let text = message.text || message.message || '';
const entities = message.entities || [];

// Format Telegram message to use bold for Discord/WhatsApp
entities.sort((a, b) => b.offset - a.offset);
for (const entity of entities) {
const start = entity.offset;
const end = entity.offset + entity.length;
if (entity.className === 'MessageEntityBold') {
  text = text.slice(0, start) + '**' + text.slice(start, end) + '**' + text.slice(end);
}
}

return text;
}

// ----------------------- MAIN -----------------------
async function start() {
console.log('Connecting to Telegram...');
await telegramClient.start();
console.log('Telegram: Logged in');

if (!TELEGRAM_STRING_SESSION) {
console.log('Telegram: Save this session string to .env:');
console.log(telegramClient.session.save());
}

telegramClient.addEventHandler(
async event => {
  const message = event.message;
  if (!message || !message.text) return;

  const content = formatMessage(message);

  // Forward to Discord
  if (discordClient) {
    try {
      const channel = await discordClient.channels.fetch(DISCORD_CHANNEL_ID);
      await channel.send(content);
      console.log('Discord: Message forwarded');
    } catch (err) {
      console.error('Discord: Failed to send message:', err);
    }
  }

  // Forward to WhatsApp
  if (whatsappClient && whatsappGroupId) {
    try {
      await whatsappClient.sendMessage(whatsappGroupId, content);
      console.log('WhatsApp: Message forwarded');
    } catch (err) {
      console.error('WhatsApp: Failed to send message:', err);
    }
  }
},
new NewMessage({ chats: [TELEGRAM_CHAT_USERNAME] })
);
}

// ----------------------- SHUTDOWN HANDLER -----------------------
async function shutdown() {
console.log('Shutting down...');

try {
await telegramClient.disconnect();
console.log('Telegram disconnected');
} catch (err) {
console.error('Telegram disconnect failed:', err);
}

if (discordClient) {
discordClient.destroy();
console.log('Discord client destroyed');
}

if (whatsappClient) {
await whatsappClient.destroy();
console.log('WhatsApp client destroyed');
}

process.exit(0);
}

process.once('SIGINT', shutdown);
process.once('SIGTERM', shutdown);

start().catch(err => {
console.error('Startup error:', err);
process.exit(1);
});

The code includes:

  • Telegram message listener
  • Discord message forwarding
  • WhatsApp Web integration
  • Message formatting conversion
  • Graceful shutdown handling

Running the Forwarder

To run your forwarder:

  1. Make sure your environment variables are set in .env
  2. Run the application:
    node index.js
    
  3. On first run:
    • If TELEGRAM_STRING_SESSION is empty, you’ll get a session string to add to .env
    • For WhatsApp, scan the QR code that appears in the terminal
  4. The forwarder will now forward messages from the specified Telegram chat to Discord and WhatsApp

The WhatsApp Web API is unofficial and may break with WhatsApp updates. For production use, consider using the official WhatsApp Business API instead.

Handling Different Message Types

The Jotihunt bot sends different types of messages. Here’s how to handle them:

Advanced Configuration

Here are some additional configuration options you might want to add:

Message Filtering

Add filters to only forward specific types of messages

Multiple Channels

Forward different message types to different channels

Error Handling

Add robust error handling and automatic reconnection

Message Queue

Implement a message queue for reliable delivery

Best Practices

  • Error Handling: Implement proper error handling for all API calls
  • Rate Limiting: Respect platform-specific rate limits
  • Logging: Add comprehensive logging for debugging
  • Message Queue: Consider using a message queue for reliability
  • Monitoring: Set up monitoring for the forwarder service
  • Testing: Test with different message types and formats

Running as a Service

To run your forwarder continuously, you can use a process manager like PM2:

# Install PM2
npm install -g pm2

# Start your forwarder
pm2 start index.js --name jotihunt-forwarder

# Monitor the process
pm2 monit

Security Considerations

When implementing the forwarder, keep these security considerations in mind:

  1. API Credentials: Keep all API tokens secure
  2. Message Validation: Validate messages before forwarding
  3. Access Control: Limit which messages get forwarded
  4. Logging: Don’t log sensitive information
  5. Updates: Keep dependencies updated

Troubleshooting

Common issues and solutions:

Conclusion

By implementing this Telegram forwarder, you can ensure your team stays updated on Jotihunt events through their preferred communication channels. Remember to monitor the forwarder’s performance and handle errors appropriately.

Credits and Inspiration

This tutorial is inspired by the jotihunt-telegram-forwarder project by ScoutingScherpenzeel, which provides a Python implementation of similar functionality. This Node.js version offers an alternative approach while maintaining the same core functionality.

Need Help?

If you have questions about the Jotihunt API or need assistance, contact the Jotihunt organization.