refactor: update message forwarding logic to support channel-based matching alongside role-based matching; enhance configuration structure to include channels; update documentation and example config

This commit is contained in:
devilreef 2026-01-08 22:06:07 +06:00
parent c4514cd4c4
commit 7ed373337f
7 changed files with 97 additions and 22 deletions

View file

@ -23,12 +23,27 @@ function loadConfig(): Config {
if (!server.guildId) {
throw new Error(`Server "${server.name}" missing guildId`)
}
if (!Array.isArray(server.roles) || server.roles.length === 0) {
throw new Error(`Server "${server.name}" missing roles array`)
const hasRoles = Array.isArray(server.roles) && server.roles.length > 0
const hasChannels = Array.isArray(server.channels) && server.channels.length > 0
if (!hasRoles && !hasChannels) {
throw new Error(`Server "${server.name}" must have roles or channels configured`)
}
for (const role of server.roles) {
if (!role.id || !role.name || typeof role.topicId !== 'number') {
throw new Error(`Invalid role config in server "${server.name}"`)
if (server.roles) {
for (const role of server.roles) {
if (!role.id || !role.name || typeof role.topicId !== 'number') {
throw new Error(`Invalid role config in server "${server.name}"`)
}
}
}
if (server.channels) {
for (const channel of server.channels) {
if (!channel.id || !channel.name || typeof channel.topicId !== 'number') {
throw new Error(`Invalid channel config in server "${server.name}"`)
}
}
}
}

View file

@ -1,5 +1,5 @@
import type { Message } from 'discord.js-selfbot-v13'
import type { AttachmentInfo, RoleConfig } from '../types.js'
import type { AttachmentInfo, ChannelConfig, RoleConfig } from '../types.js'
import { config } from '../config.js'
import { forwardMessage } from '../telegram/sender.js'
import { discord } from './client.js'
@ -8,6 +8,12 @@ export function setupHandlers(): void {
discord.on('messageCreate', handleMessage)
}
interface MatchResult {
topicId: number
label: string
type: 'channel' | 'role'
}
async function handleMessage(message: Message): Promise<void> {
if (!message.guild || !message.member)
return
@ -16,8 +22,24 @@ async function handleMessage(message: Message): Promise<void> {
if (!serverConfig)
return
const matchedRole = findHighestPriorityRole(message.member.roles.cache, serverConfig.roles)
if (!matchedRole)
// Check channels first (higher priority), then roles
let match: MatchResult | null = null
if (serverConfig.channels) {
const channelMatch = findMatchingChannel(message.channel.id, serverConfig.channels)
if (channelMatch) {
match = { topicId: channelMatch.topicId, label: channelMatch.name, type: 'channel' }
}
}
if (!match && serverConfig.roles) {
const roleMatch = findHighestPriorityRole(message.member.roles.cache, serverConfig.roles)
if (roleMatch) {
match = { topicId: roleMatch.topicId, label: roleMatch.name, type: 'role' }
}
}
if (!match)
return
const attachments: AttachmentInfo[] = message.attachments.map(att => ({
@ -27,24 +49,32 @@ async function handleMessage(message: Message): Promise<void> {
}))
const messageLink = `https://discord.com/channels/${message.guild.id}/${message.channel.id}/${message.id}`
const channelName = 'name' in message.channel ? (message.channel.name ?? 'unknown') : 'DM'
try {
await forwardMessage({
topicId: matchedRole.topicId,
topicId: match.topicId,
author: message.author.displayName ?? message.author.username,
role: matchedRole.name,
channel: 'name' in message.channel ? (message.channel.name ?? 'unknown') : 'DM',
role: match.type === 'role' ? match.label : null,
channel: channelName,
content: message.content,
attachments,
messageLink,
})
console.log(`Forwarded message from ${message.author.tag} (${matchedRole.name}) in ${serverConfig.name}`)
console.log(`Forwarded message from ${message.author.tag} (${match.type}: ${match.label}) in ${serverConfig.name}`)
}
catch (err) {
console.error('Failed to forward message:', err)
}
}
function findMatchingChannel(
channelId: string,
configChannels: ChannelConfig[],
): ChannelConfig | null {
return configChannels.find(ch => ch.id === channelId) ?? null
}
function findHighestPriorityRole(
memberRoles: Map<string, unknown>,
configRoles: RoleConfig[],

View file

@ -15,7 +15,8 @@ interface DownloadedAttachment {
export async function forwardMessage(opts: ForwardMessageOptions): Promise<void> {
const { topicId, author, role, channel, content, attachments, messageLink } = opts
const text = `<b>${escapeHtml(author)}</b> (${escapeHtml(role)}) in <code>#${escapeHtml(channel)}</code>\n${escapeHtml(content)}\n\n<a href="${messageLink}">Jump to message</a>`
const roleText = role ? ` (${escapeHtml(role)})` : ''
const text = `<b>${escapeHtml(author)}</b>${roleText} in <code>#${escapeHtml(channel)}</code>\n${escapeHtml(content)}\n\n<a href="${messageLink}">Jump to message</a>`
await telegram.api.sendMessage({
chat_id: config.telegram.chatId,

View file

@ -4,10 +4,17 @@ export interface RoleConfig {
topicId: number
}
export interface ChannelConfig {
id: string
name: string
topicId: number
}
export interface ServerConfig {
name: string
guildId: string
roles: RoleConfig[]
roles?: RoleConfig[]
channels?: ChannelConfig[]
}
export interface Config {
@ -20,7 +27,7 @@ export interface Config {
export interface ForwardMessageOptions {
topicId: number
author: string
role: string
role: string | null
channel: string
content: string
attachments: AttachmentInfo[]