Skip to content

Commit

Permalink
update logic for sending messages
Browse files Browse the repository at this point in the history
* remove `/` from commands
* fix invisible space character in mentions
* bump packages
  • Loading branch information
destruc7i0n committed Mar 14, 2022
1 parent 8a4a5bf commit 1182a33
Show file tree
Hide file tree
Showing 5 changed files with 783 additions and 615 deletions.
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@
},
"homepage": "https://github.com/destruc7i0n/shulker#readme",
"dependencies": {
"axios": "^0.24.0",
"discord.js": "13.4.0",
"axios": "^0.26.1",
"discord.js": "13.6.0",
"emoji-strip": "^1.0.1",
"express": "^4.17.1",
"express": "^4.17.3",
"tail": "^2.2.4"
},
"devDependencies": {
"@types/emoji-strip": "^1.0.0",
"@types/express": "^4.17.13",
"@types/jest": "^27.4.0",
"@types/node": "^16.11.12",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@types/tail": "^2.2.1",
"dotenv": "^10.0.0",
"jest": "^27.4.5",
"ts-jest": "^27.1.2",
"typescript": "^4.5.3"
"dotenv": "^16.0.0",
"jest": "^27.5.1",
"ts-jest": "^27.1.3",
"typescript": "^4.6.2"
}
}
68 changes: 36 additions & 32 deletions src/Discord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,35 +88,20 @@ class Discord {
if (message.author.id === this.client.user?.id) return
// ignore any attachments
if (message.attachments.size) return

const command = this.buildMinecraftCommand(message)

if (command) {
if (this.config.DEBUG) console.log(`[DEBUG] Sending command "${command}" to the server`)

const rcon = new Rcon(this.config.MINECRAFT_SERVER_RCON_IP, this.config.MINECRAFT_SERVER_RCON_PORT, this.config.DEBUG)
try {
await rcon.auth(this.config.MINECRAFT_SERVER_RCON_PASSWORD)
} catch (e) {
console.log('[ERROR] Could not auth with the server!')
if (this.config.DEBUG) console.error(e)
}

let command: string | undefined;
if (this.config.ALLOW_SLASH_COMMANDS && this.config.SLASH_COMMAND_ROLES_IDS && message.cleanContent.startsWith('/') && message.member) {
const hasSlashCommandRole = this.config.SLASH_COMMAND_ROLES_IDS.some(id => message.member?.roles.cache.get(id))
if (hasSlashCommandRole) {
// send the raw command, can be dangerous...
command = message.cleanContent
} else {
console.log('[INFO] User attempted a slash command without a role')
}
} else {
if (this.config.MINECRAFT_TELLRAW_DOESNT_EXIST) {
command = `/say ${this.makeMinecraftMessage(message)}`
} else {
command = `/tellraw @a ${this.makeMinecraftMessage(message)}`
const rcon = new Rcon(this.config.MINECRAFT_SERVER_RCON_IP, this.config.MINECRAFT_SERVER_RCON_PORT, this.config.DEBUG)
try {
await rcon.auth(this.config.MINECRAFT_SERVER_RCON_PASSWORD)
} catch (e) {
console.log('[ERROR] Could not auth with the server!')
if (this.config.DEBUG) console.error(e)
}
}

if (this.config.DEBUG) console.log(`[DEBUG] Sending command "${command}" to the server`)

if (command) {
let response: string | undefined;
try {
response = await rcon.command(command)
Expand All @@ -127,30 +112,48 @@ class Discord {

if (response?.startsWith('Unknown command') || response?.startsWith('Unknown or incomplete command')) {
console.log('[ERROR] Could not send command! (Unknown command)')
if (command.startsWith('/tellraw')) {
if (command.startsWith('tellraw')) {
console.log('[INFO] Your Minecraft version may not support tellraw, please check MINECRAFT_TELLRAW_DOESNT_EXIST in the README')
}
}

rcon.close()
}
rcon.close()
}

private makeMinecraftMessage(message: Message): string {
private buildMinecraftCommand (message: Message) {
if (this.config.ALLOW_SLASH_COMMANDS && this.config.SLASH_COMMAND_ROLES_IDS && message.cleanContent.startsWith('/') && message.member) {
const hasSlashCommandRole = this.config.SLASH_COMMAND_ROLES_IDS.some(id => message.member?.roles.cache.get(id))
if (hasSlashCommandRole) {
// send the raw command, can be dangerous...
return message.cleanContent
} else {
const { username, discriminator } = message.author
console.log(`[INFO] User ${username}#${discriminator} attempted to send a slash command without a role`)
return
}
}

const username = emojiStrip(message.author.username)

let messageContent = message.cleanContent
messageContent = messageContent.replace('@\u200b', '@')
messageContent = emojiStrip(messageContent)

const variables: {[index: string]: string} = {
username,
nickname: !!message.member?.nickname ? emojiStrip(message.member.nickname) : username,
discriminator: message.author.discriminator,
text: emojiStrip(message.cleanContent),
text: messageContent,
}

if (this.config.MINECRAFT_TELLRAW_DOESNT_EXIST) {
return this.config.MINECRAFT_TELLRAW_DOESNT_EXIST_SAY_TEMPLATE
const sayMessage = this.config.MINECRAFT_TELLRAW_DOESNT_EXIST_SAY_TEMPLATE
.replace(/%username%/g, variables.username)
.replace(/%nickname%/g, variables.nickname)
.replace(/%discriminator%/g, variables.discriminator)
.replace(/%message%/g, variables.text)
return `say ${sayMessage}`
}

for (const [k, v] of Object.entries(variables)) {
Expand All @@ -160,11 +163,12 @@ class Discord {
variables[k] = escapeUnicode(stringified)
}

return this.config.MINECRAFT_TELLRAW_TEMPLATE
const tellrawComponent = this.config.MINECRAFT_TELLRAW_TEMPLATE
.replace(/%username%/g, variables.username)
.replace(/%nickname%/g, variables.nickname)
.replace(/%discriminator%/g, variables.discriminator)
.replace(/%message%/g, variables.text)
return `tellraw @a ${tellrawComponent}`
}

private async replaceDiscordMentions(message: string): Promise<string> {
Expand Down
34 changes: 24 additions & 10 deletions tests/Discord.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,36 +44,50 @@ describe('Discord', () => {
it('correctly generates a tellraw string in messages', async () => {
const discord = new Discord(defaultConfig)

const message = discord['makeMinecraftMessage']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'test' } as any)
expect(message).toBe('[{"color": "white", "text": "<@destruc7i0n> test"}]')
const message = discord['buildMinecraftCommand']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'test' } as any)
expect(message).toBe('tellraw @a [{"color": "white", "text": "<@destruc7i0n> test"}]')
})

it('correctly generates a tellraw string with unicode characters in message', async () => {
const discord = new Discord(defaultConfig)

const message = discord['makeMinecraftMessage']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'æ, ø, å (Æ, Ø, Å) 건희' } as any)
expect(message).toBe('[{"color": "white", "text": "<@destruc7i0n> \\u00e6, \\u00f8, \\u00e5 (\\u00c6, \\u00d8, \\u00c5) \\uac74\\ud76c"}]')
const message = discord['buildMinecraftCommand']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'æ, ø, å (Æ, Ø, Å) 건희' } as any)
expect(message).toBe('tellraw @a [{"color": "white", "text": "<@destruc7i0n> \\u00e6, \\u00f8, \\u00e5 (\\u00c6, \\u00d8, \\u00c5) \\uac74\\ud76c"}]')
})

it('correctly generates a tellraw string with unicode characters in username ', async () => {
const discord = new Discord(defaultConfig)

const message = discord['makeMinecraftMessage']({ author: { username: '건희destruc7i0n건희', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'huh' } as any)
expect(message).toBe('[{"color": "white", "text": "<@\\uac74\\ud76cdestruc7i0n\\uac74\\ud76c> huh"}]')
const message = discord['buildMinecraftCommand']({ author: { username: '건희destruc7i0n건희', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'huh' } as any)
expect(message).toBe('tellraw @a [{"color": "white", "text": "<@\\uac74\\ud76cdestruc7i0n\\uac74\\ud76c> huh"}]')
})

it('correctly replaces all parts of a message string', () => {
const discord = new Discord({ ...defaultConfig, MINECRAFT_TELLRAW_TEMPLATE: '[{\"color\": \"white\", \"text\": \"<@%username%#%discriminator% (%nickname%)> %message%\"}]' })

const message = discord['makeMinecraftMessage']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'test' } as any)
expect(message).toBe('[{\"color\": \"white\", \"text\": \"<@destruc7i0n#7070 (t70)> test\"}]')
const message = discord['buildMinecraftCommand']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70' }, cleanContent: 'test' } as any)
expect(message).toBe('tellraw @a [{\"color\": \"white\", \"text\": \"<@destruc7i0n#7070 (t70)> test\"}]')
})

it('correctly replaces all parts of a message string with unicode everywhere', () => {
const discord = new Discord({ ...defaultConfig, MINECRAFT_TELLRAW_TEMPLATE: '[{\"color\": \"white\", \"text\": \"<@%username%#%discriminator% (%nickname%)> %message%\"}]' })

const message = discord['makeMinecraftMessage']({ author: { username: '건희destruc7i0n건희', discriminator: '7070' }, member: { nickname: '건희t70건희' }, cleanContent: 'æ, ø, å (Æ, Ø, Å) 건희' } as any)
expect(message).toBe('[{\"color\": \"white\", \"text\": \"<@\\uac74\\ud76cdestruc7i0n\\uac74\\ud76c#7070 (\\uac74\\ud76ct70\\uac74\\ud76c)> \\u00e6, \\u00f8, \\u00e5 (\\u00c6, \\u00d8, \\u00c5) \\uac74\\ud76c\"}]')
const message = discord['buildMinecraftCommand']({ author: { username: '건희destruc7i0n건희', discriminator: '7070' }, member: { nickname: '건희t70건희' }, cleanContent: 'æ, ø, å (Æ, Ø, Å) 건희' } as any)
expect(message).toBe('tellraw @a [{\"color\": \"white\", \"text\": \"<@\\uac74\\ud76cdestruc7i0n\\uac74\\ud76c#7070 (\\uac74\\ud76ct70\\uac74\\ud76c)> \\u00e6, \\u00f8, \\u00e5 (\\u00c6, \\u00d8, \\u00c5) \\uac74\\ud76c\"}]')
})

it('does not return command when sending slash command and user does not have role', async () => {
const discord = new Discord({ ...defaultConfig, ALLOW_SLASH_COMMANDS: true, SLASH_COMMAND_ROLES_IDS: ['123'] })

const message = discord['buildMinecraftCommand']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70', roles: { cache: new Map([]) } }, cleanContent: '/say destruc7i0n' } as any)
expect(message).toBeUndefined()
})

it('returns command when sending slash command and user has role', async () => {
const discord = new Discord({ ...defaultConfig, ALLOW_SLASH_COMMANDS: true, SLASH_COMMAND_ROLES_IDS: ['123'] })

const message = discord['buildMinecraftCommand']({ author: { username: 'destruc7i0n', discriminator: '7070' }, member: { nickname: 't70', roles: { cache: new Map([['123', true]]) } }, cleanContent: '/say destruc7i0n' } as any)
expect(message).toBe('/say destruc7i0n')
})
})
})
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
"baseUrl": "."
},
"include": ["**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "tests"]
}
Loading

0 comments on commit 1182a33

Please sign in to comment.