Added Intents.None, partial support for selfbot, required changes

This commit is contained in:
DjDeveloperr 2020-11-06 16:12:00 +05:30
parent 4228cd8f52
commit 3a3af635c6
8 changed files with 137 additions and 33 deletions

View File

@ -1,5 +1,7 @@
# discord-deno # discord-deno
![banner](banner.png)
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
**An easy to use Discord API Library for Deno.** **An easy to use Discord API Library for Deno.**
@ -36,16 +38,19 @@ import { Client, Message, Intents } from 'https://raw.githubusercontent.com/disc
const client = new Client() const client = new Client()
// Listen for event when client is ready (Identified through gateway / Resumed)
client.on('ready', () => { client.on('ready', () => {
console.log(`Ready! User: ${client.user?.tag}`) console.log(`Ready! User: ${client.user?.tag}`)
}) })
// Listen for event whenever a Message is sent
client.on('messageCreate', (msg: Message): void => { client.on('messageCreate', (msg: Message): void => {
if (msg.content === '!ping') { if (msg.content === '!ping') {
msg.channel.send(`Pong! WS Ping: ${client.ping}`) msg.channel.send(`Pong! WS Ping: ${client.ping}`)
} }
}) })
// Connect to gateway
// Replace with your bot's token and intents (Intents.All, Intents.Presence, Intents.GuildMembers) // Replace with your bot's token and intents (Intents.All, Intents.Presence, Intents.GuildMembers)
client.connect('super secret token comes here', Intents.All) client.connect('super secret token comes here', Intents.All)
``` ```

View File

@ -21,8 +21,8 @@ if(!token) {
Deno.exit(); Deno.exit();
} }
const intents = prompt("Input Intents (0 = All, 1 = Presence, 2 = Server Members):"); const intents = prompt("Input Intents (0 = All, 1 = Presence, 2 = Server Members, 3 = None):");
if(!intents || !["0", "1", "2"].includes(intents)) { if(!intents || !["0", "1", "2", "3"].includes(intents)) {
console.log("No intents provided"); console.log("No intents provided");
Deno.exit(); Deno.exit();
} }
@ -32,8 +32,10 @@ if(intents == "0") {
ints = Intents.All; ints = Intents.All;
} else if(intents == "1") { } else if(intents == "1") {
ints = Intents.Presence; ints = Intents.Presence;
} else { } else if(intents == "2") {
ints = Intents.GuildMembers; ints = Intents.GuildMembers;
} else {
ints = Intents.None;
} }
client.connect(token, ints); client.connect(token, ints);

View File

@ -1,4 +1,4 @@
import { unzlib } from 'https://deno.land/x/denoflate/mod.ts' import { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts'
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { import {
DISCORD_GATEWAY_URL, DISCORD_GATEWAY_URL,
@ -140,7 +140,7 @@ class Gateway {
} }
} }
private onclose (event: CloseEvent): void { private onclose(event: CloseEvent): void {
this.debug(`Connection Closed with code: ${event.code}`) this.debug(`Connection Closed with code: ${event.code}`)
if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) { if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) {
@ -189,28 +189,31 @@ class Gateway {
console.log(eventError) console.log(eventError)
} }
private async sendIdentify (forceNewSession?: boolean): Promise<void> { private async sendIdentify(forceNewSession?: boolean): Promise<void> {
this.debug('Fetching /gateway/bot...') if (this.client.bot === true) {
const info = await this.client.rest.get(GATEWAY_BOT()) this.debug('Fetching /gateway/bot...')
if (info.session_start_limit.remaining === 0) const info = await this.client.rest.get(GATEWAY_BOT())
throw new Error( if (info.session_start_limit.remaining === 0)
`Session Limit Reached. Retry After ${info.session_start_limit.reset_after}ms` throw new Error(
`Session Limit Reached. Retry After ${info.session_start_limit.reset_after}ms`
)
this.debug(`Recommended Shards: ${info.shards}`)
this.debug('=== Session Limit Info ===')
this.debug(
`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}`
) )
this.debug(`Recommended Shards: ${info.shards}`) this.debug(`Reset After: ${info.session_start_limit.reset_after}ms`)
this.debug('=== Session Limit Info ===') if (forceNewSession === undefined || !forceNewSession) {
this.debug( const sessionIDCached = await this.cache.get('session_id')
`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}` if (sessionIDCached !== undefined) {
) this.debug(`Found Cached SessionID: ${sessionIDCached}`)
this.debug(`Reset After: ${info.session_start_limit.reset_after}ms`) this.sessionID = sessionIDCached
if (forceNewSession === undefined || !forceNewSession) { return await this.sendResume()
const sessionIDCached = await this.cache.get('session_id') }
if (sessionIDCached !== undefined) {
this.debug(`Found Cached SessionID: ${sessionIDCached}`)
this.sessionID = sessionIDCached
return await this.sendResume()
} }
} }
this.send({
let payload: any = {
op: GatewayOpcodes.IDENTIFY, op: GatewayOpcodes.IDENTIFY,
d: { d: {
token: this.token, token: this.token,
@ -227,7 +230,24 @@ class Gateway {
), ),
presence: this.client.presence.create() presence: this.client.presence.create()
} }
}) }
if(this.client.bot === false) {
// TODO: Complete Selfbot support
this.debug("Modify Identify Payload for Self-bot..")
// delete payload.d['intents']
// payload.d.intents = Intents.None
payload.d.presence = null
payload.d.properties = {
$os: "Windows",
$browser: "Firefox",
$device: ""
}
this.debug("Warn: Support for selfbots is incomplete")
}
this.send(payload)
} }
private async sendResume(): Promise<void> { private async sendResume(): Promise<void> {
@ -248,11 +268,11 @@ class Gateway {
this.send(resumePayload) this.send(resumePayload)
} }
debug (msg: string): void { debug(msg: string): void {
this.client.debug('Gateway', msg) this.client.debug('Gateway', msg)
} }
async reconnect (forceNew?: boolean): Promise<void> { async reconnect(forceNew?: boolean): Promise<void> {
clearInterval(this.heartbeatIntervalID) clearInterval(this.heartbeatIntervalID)
if (forceNew === undefined || !forceNew) if (forceNew === undefined || !forceNew)
await this.cache.delete('session_id') await this.cache.delete('session_id')
@ -315,7 +335,7 @@ class Gateway {
return return
} }
this.sendHeartbeat() this.sendHeartbeat()
} }
} }

View File

@ -18,6 +18,8 @@ export interface ClientOptions {
cache?: ICacheAdapter, cache?: ICacheAdapter,
forceNewSession?: boolean, forceNewSession?: boolean,
presence?: ClientPresence | ClientActivity | ActivityGame presence?: ClientPresence | ClientActivity | ActivityGame
bot?: boolean
canary?: boolean
} }
/** /**
@ -37,6 +39,8 @@ export class Client extends EventEmitter {
channels: ChannelsManager = new ChannelsManager(this) channels: ChannelsManager = new ChannelsManager(this)
messages: MessagesManager = new MessagesManager(this) messages: MessagesManager = new MessagesManager(this)
emojis: EmojisManager = new EmojisManager(this) emojis: EmojisManager = new EmojisManager(this)
bot: boolean = true
canary: boolean = false
presence: ClientPresence = new ClientPresence() presence: ClientPresence = new ClientPresence()
@ -47,6 +51,8 @@ export class Client extends EventEmitter {
this.forceNewSession = options.forceNewSession this.forceNewSession = options.forceNewSession
if (options.cache !== undefined) this.cache = options.cache if (options.cache !== undefined) this.cache = options.cache
if (options.presence !== undefined) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence) if (options.presence !== undefined) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence)
if (options.bot === false) this.bot = false
if (options.canary === true) this.canary = true
} }
setAdapter (adapter: ICacheAdapter): Client { setAdapter (adapter: ICacheAdapter): Client {

View File

@ -1,6 +1,7 @@
import { delay } from '../utils/index.ts' import { delay } from '../utils/index.ts'
import * as baseEndpoints from '../consts/urlsAndVersions.ts' import * as baseEndpoints from '../consts/urlsAndVersions.ts'
import { Client } from './client.ts' import { Client } from './client.ts'
import { getBuildInfo } from "../utils/buildInfo.ts"
export enum HttpResponseCode { export enum HttpResponseCode {
Ok = 200, Ok = 200,
@ -175,11 +176,29 @@ export class RESTManager {
headers['Content-Type'] = 'application/json' headers['Content-Type'] = 'application/json'
} }
return { let data: { [name: string]: any } = {
headers, headers,
body: body?.file ?? JSON.stringify(body), body: body?.file ?? JSON.stringify(body),
method: method.toUpperCase() method: method.toUpperCase()
} }
if(this.client.bot === false) {
// This is a selfbot. Use requests similar to Discord Client
data.headers['authorization'] = this.client.token as string
data.headers['accept-language'] = 'en-US'
data.headers['accept'] = '*/*'
data.headers['sec-fetch-dest'] = 'empty'
data.headers['sec-fetch-mode'] = 'cors'
data.headers['sec-fetch-site'] = 'same-origin'
data.headers['x-super-properties'] = btoa(JSON.stringify(getBuildInfo(this.client)))
delete data.headers['User-Agent']
delete data.headers['Authorization']
headers['credentials'] = 'include'
headers['mode'] = 'cors'
headers['referrerPolicy'] = 'no-referrer-when-downgrade'
}
return data
} }
async checkRatelimits (url: string): Promise<number | false> { async checkRatelimits (url: string): Promise<number | false> {
@ -230,9 +249,14 @@ export class RESTManager {
) )
.join('&') .join('&')
: '' : ''
const urlToUse = let urlToUse =
method === 'get' && query !== '' ? `${url}?${query}` : url method === 'get' && query !== '' ? `${url}?${query}` : url
if(this.client.canary) {
let split = urlToUse.split('//')
urlToUse = split[0] + '//canary.' + split[1]
}
const requestData = this.createRequestBody(body, method) const requestData = this.createRequestBody(body, method)
const response = await fetch( const response = await fetch(

View File

@ -1,5 +1,4 @@
import { Client, GuildTextChannel, Message, RedisCacheAdapter, ClientPresence, Member, Role, GuildChannel, TextChannel, Embed, Guild } from '../../mod.ts'; import { Client, Intents, GuildTextChannel, Message, ClientPresence, Member, Role, GuildChannel, Embed, Guild } from '../../mod.ts';
import { Intents } from "../utils/intents.ts";
import { TOKEN } from './config.ts' import { TOKEN } from './config.ts'
const client = new Client({ const client = new Client({
@ -7,6 +6,7 @@ const client = new Client({
name: 'Pokémon Sword', name: 'Pokémon Sword',
type: 'COMPETING' type: 'COMPETING'
}), }),
// bot: false,
// cache: new RedisCacheAdapter({ // cache: new RedisCacheAdapter({
// hostname: '127.0.0.1', // hostname: '127.0.0.1',
// port: 6379 // port: 6379
@ -30,6 +30,7 @@ client.on('channelUpdate', (before: GuildTextChannel, after: GuildTextChannel) =
client.on('messageCreate', async (msg: Message) => { client.on('messageCreate', async (msg: Message) => {
if (msg.author.bot === true) return if (msg.author.bot === true) return
console.log(`${msg.author.tag}: ${msg.content}`)
if (msg.content === '!ping') { if (msg.content === '!ping') {
msg.reply(`Pong! Ping: ${client.ping}ms`) msg.reply(`Pong! Ping: ${client.ping}ms`)
} else if (msg.content === '!members') { } else if (msg.content === '!members') {
@ -58,4 +59,4 @@ client.on('messageCreate', async (msg: Message) => {
} }
}) })
client.connect(TOKEN, Intents.All) client.connect(TOKEN, Intents.None)

30
src/utils/buildInfo.ts Normal file
View File

@ -0,0 +1,30 @@
import { Client } from "../models/client.ts";
export const getBuildInfo = (client: Client) => {
let os = 'Windows'
let os_version = '10'
let client_build_number = 71073
let client_event_source = null
let release_channel = 'stable'
if (client.canary) {
release_channel = 'canary'
client_build_number = 71076
}
let browser = 'Firefox'
let browser_version = '83.0'
let browser_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 ' + browser + '/' + browser_version
// TODO: Use current OS properties, but also browser_user_agent accordingly
// if(Deno.build.os === 'darwin') os = 'MacOS'
// else if(Deno.build.os === 'linux') os = 'Ubuntu'
return {
os,
os_version,
browser,
browser_version,
browser_user_agent,
client_build_number,
client_event_source,
release_channel,
}
};

View File

@ -52,4 +52,20 @@ export class Intents {
GatewayIntents.GUILD_VOICE_STATES, GatewayIntents.GUILD_VOICE_STATES,
GatewayIntents.GUILD_WEBHOOKS GatewayIntents.GUILD_WEBHOOKS
]; ];
static None: number[] = [
GatewayIntents.GUILD_MESSAGES,
GatewayIntents.DIRECT_MESSAGES,
GatewayIntents.DIRECT_MESSAGE_REACTIONS,
GatewayIntents.DIRECT_MESSAGE_TYPING,
GatewayIntents.GUILDS,
GatewayIntents.GUILD_BANS,
GatewayIntents.GUILD_EMOJIS,
GatewayIntents.GUILD_INTEGRATIONS,
GatewayIntents.GUILD_INVITES,
GatewayIntents.GUILD_MESSAGE_REACTIONS,
GatewayIntents.GUILD_MESSAGE_TYPING,
GatewayIntents.GUILD_VOICE_STATES,
GatewayIntents.GUILD_WEBHOOKS
]
} }