Merge branch 'main' into main

This commit is contained in:
DjDeveloper 2020-11-08 14:13:45 +05:30 committed by GitHub
commit b15c5fb79d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 363 additions and 220 deletions

36
.github/workflows/deno.yml vendored Normal file
View file

@ -0,0 +1,36 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will install Deno and run tests across stable and nightly builds on Windows, Ubuntu and macOS.
# For more information see: https://github.com/denolib/setup-deno
name: Run in Deno
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ${{ matrix.os }} # runs a test on Ubuntu, Windows and macOS
strategy:
matrix:
deno: ["v1.x", "nightly"]
os: [macOS-latest, windows-latest, ubuntu-latest]
steps:
- name: Setup repo
uses: actions/checkout@v2
- name: Setup Deno
uses: denolib/setup-deno@c7d7968ad4a59c159a777f79adddad6872ee8d96
with:
deno-version: ${{ matrix.deno }} # tests across multiple Deno versions
- name: Run mod.ts to test deeper
run: deno run --allow-net mod.ts

31
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,31 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Lint Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macOS-latest, windows-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2.1.2
- name: Setup ESLint
run: npm install
- name: Test ESLint
run: npx eslint src/

View file

@ -16,6 +16,18 @@ const example = (): void => {}
``` ```
- Do not make unused variables or unused imports. - Do not make unused variables or unused imports.
- Make a space before a new block, Parentheses, equal, etc. Ex:
```ts
//Here Here
if (true) {
}
// Here Here
const example = ''
```
- Use 'single quote' instead of "double quote".
These are not on standard.js but we want you to follow. These are not on standard.js but we want you to follow.

View file

@ -1,4 +1,4 @@
# discord-deno # harmony
![banner](banner.png) ![banner](banner.png)
@ -28,17 +28,17 @@ Note: Library is yet under development and not completely usable. You're still a
## Usage ## Usage
Right now, the package is not published anywhere, as its not completely usable. Right now, the package is not published anywhere, as its not completely usable.
You can import it from this Raw GitHub URL: https://raw.githubusercontent.com/discord-deno/discord.deno/main/mod.ts You can import it from this Raw GitHub URL: https://raw.githubusercontent.com/harmony-org/harmony/main/mod.ts
For a quick example, run this: For a quick example, run this:
```bash ```bash
deno run --allow-net https://raw.githubusercontent.com/discord-deno/discord.deno/main/examples/ping.ts deno run --allow-net https://raw.githubusercontent.com/harmony-org/harmony/main/examples/ping.ts
``` ```
And input your bot's token and Intents. And input your bot's token and Intents.
Here is a small example of how to use discord.deno, Here is a small example of how to use harmony,
```ts ```ts
import { Client, Message, Intents } from 'https://raw.githubusercontent.com/discord-deno/discord.deno/main/mod.ts' import { Client, Message, Intents } from 'https://raw.githubusercontent.com/harmony-org/harmony/main/mod.ts'
const client = new Client() const client = new Client()
@ -61,7 +61,7 @@ client.connect('super secret token comes here', Intents.All)
Or with CommandClient! Or with CommandClient!
```ts ```ts
import { CommandClient, Command, CommandContext, Message, Intents } from 'https://raw.githubusercontent.com/discord-deno/discord.deno/main/mod.ts' import { CommandClient, Command, CommandContext, Message, Intents } from 'https://raw.githubusercontent.com/harmony-org/harmony/main/mod.ts'
const client = new CommandClient({ const client = new CommandClient({
prefix: '!' prefix: '!'

BIN
banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -1,41 +1,43 @@
import { Client, Message, Intents } from '../mod.ts' import { Client, Message, Intents } from '../mod.ts'
const client = new Client(); const client = new Client()
client.on("ready", () => { client.on('ready', () => {
console.log(`Logged in as ${client.user?.tag}!`); console.log(`Logged in as ${client.user?.tag}!`)
}); })
client.on("messageCreate", (msg: Message) => { client.on('messageCreate', (msg: Message) => {
if (msg.content === "!ping") { if (msg.content === '!ping') {
console.log("Command Used: Ping"); console.log('Command Used: Ping')
msg.reply("pong!"); msg.reply('pong!')
} }
}); })
console.log("discord.deno - ping example"); console.log('harmony - ping example')
const token = prompt("Input Bot Token:"); const token = prompt('Input Bot Token:')
if (token === null) { if (token === null) {
console.log("No token provided"); console.log('No token provided')
Deno.exit(); Deno.exit()
} }
const intents = prompt("Input Intents (0 = All, 1 = Presence, 2 = Server Members, 3 = None):"); const intents = prompt(
if (intents === null || !["0", "1", "2", "3"].includes(intents)) { 'Input Intents (0 = All, 1 = Presence, 2 = Server Members, 3 = None):'
console.log("No intents provided"); )
Deno.exit(); if (intents === null || !['0', '1', '2', '3'].includes(intents)) {
console.log('No intents provided')
Deno.exit()
} }
let ints; let ints
if (intents === "0") { if (intents === '0') {
ints = Intents.All; ints = Intents.All
} else if (intents === "1") { } else if (intents === '1') {
ints = Intents.Presence; ints = Intents.Presence
} else if (intents === "2") { } else if (intents === '2') {
ints = Intents.GuildMembers; ints = Intents.GuildMembers
} else { } else {
ints = Intents.None; ints = Intents.None
} }
client.connect(token, ints); client.connect(token, ints)

View file

@ -1,9 +1,9 @@
{ {
"name": "discord.deno", "name": "harmony",
"version": "1.0.0", "version": "1.0.0",
"description": "Discord Deno API that is easy to use.", "description": "Discord Deno API that is easy to use.",
"main": "index.js", "main": "index.js",
"repository": "https://github.com/Helloyunho/discord.deno.git", "repository": "https://github.com/harmony/harmony.git",
"author": "Helloyunho <yunho050840@gmail.com>", "author": "Helloyunho <yunho050840@gmail.com>",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {

View file

@ -6,47 +6,58 @@ import { GuildChannelPayload } from '../../types/channel.ts'
import { RolePayload } from '../../types/role.ts' import { RolePayload } from '../../types/role.ts'
import { RolesManager } from '../../managers/roles.ts' import { RolesManager } from '../../managers/roles.ts'
export const guildCreate: GatewayEventHandler = async(gateway: Gateway, d: GuildPayload) => { export const guildCreate: GatewayEventHandler = async (
gateway: Gateway,
d: GuildPayload
) => {
let guild: Guild | undefined = await gateway.client.guilds.get(d.id) let guild: Guild | undefined = await gateway.client.guilds.get(d.id)
if (guild !== undefined) { if (guild !== undefined) {
// It was just lazy load, so we don't fire the event as its gonna fire for every guild bot is in // It was just lazy load, so we don't fire the event as its gonna fire for every guild bot is in
await gateway.client.guilds.set(d.id, d) await gateway.client.guilds.set(d.id, d)
if (d.members !== undefined) { if (d.members !== undefined) {
const members = new MembersManager(gateway.client, guild) const members = new MembersManager(gateway.client, guild)
await members.fromPayload(d.members as MemberPayload[]) await members.fromPayload(d.members as MemberPayload[])
guild.members = members guild.members = members
} }
if (d.channels !== undefined) { if (d.channels !== undefined) {
for (const ch of d.channels as GuildChannelPayload[]) { for (const ch of d.channels as GuildChannelPayload[]) {
ch.guild_id = d.id ch.guild_id = d.id
await gateway.client.channels.set(ch.id, ch) await gateway.client.channels.set(ch.id, ch)
} }
} }
if (d.roles !== undefined) { if (d.roles !== undefined) {
const roles = new RolesManager(gateway.client, guild) const roles = new RolesManager(gateway.client, guild)
await roles.fromPayload(d.roles as RolePayload[]) await roles.fromPayload(d.roles as RolePayload[])
guild.roles = roles guild.roles = roles
} }
guild.refreshFromData(d) guild.refreshFromData(d)
} else { } else {
await gateway.client.guilds.set(d.id, d) await gateway.client.guilds.set(d.id, d)
guild = new Guild(gateway.client, d) guild = new Guild(gateway.client, d)
if ((d as any).members !== undefined) { if ((d as any).members !== undefined) {
const members = new MembersManager(gateway.client, guild) const members = new MembersManager(gateway.client, guild)
await members.fromPayload(d.members as MemberPayload[]) await members.fromPayload(d.members as MemberPayload[])
guild.members = members guild.members = members
} }
if (d.channels !== undefined) { if (d.channels !== undefined) {
for (const ch of d.channels as GuildChannelPayload[]) { for (const ch of d.channels as GuildChannelPayload[]) {
(ch as any).guild_id = d.id ;(ch as any).guild_id = d.id
await gateway.client.channels.set(ch.id, ch) await gateway.client.channels.set(ch.id, ch)
} }
} }
if (d.roles !== undefined) { if (d.roles !== undefined) {
const roles = new RolesManager(gateway.client, guild) const roles = new RolesManager(gateway.client, guild)
await roles.fromPayload(d.roles) await roles.fromPayload(d.roles)
guild.roles = roles guild.roles = roles
} }
await guild.roles.fromPayload(d.roles) await guild.roles.fromPayload(d.roles)
guild = new Guild(gateway.client, d) guild = new Guild(gateway.client, d)
gateway.client.emit('guildCreate', guild) gateway.client.emit('guildCreate', guild)

View file

@ -10,10 +10,12 @@ export const guildDelte: GatewayEventHandler = async (
if (guild !== undefined) { if (guild !== undefined) {
guild.refreshFromData(d) guild.refreshFromData(d)
await guild.members.flush() await guild.members.flush()
await guild.channels.flush() await guild.channels.flush()
await guild.roles.flush() await guild.roles.flush()
await gateway.client.guilds.delete(d.id) await gateway.client.guilds.delete(d.id)
gateway.client.emit('guildDelete', guild) gateway.client.emit('guildDelete', guild)
} }
} }

View file

@ -36,7 +36,7 @@ class Gateway {
client: Client client: Client
cache: GatewayCache cache: GatewayCache
constructor(client: Client, token: string, intents: GatewayIntents[]) { constructor (client: Client, token: string, intents: GatewayIntents[]) {
this.token = token this.token = token
this.intents = intents this.intents = intents
this.client = client this.client = client
@ -53,12 +53,12 @@ class Gateway {
this.websocket.onerror = this.onerror.bind(this) this.websocket.onerror = this.onerror.bind(this)
} }
private onopen(): void { private onopen (): void {
this.connected = true this.connected = true
this.debug('Connected to Gateway!') this.debug('Connected to Gateway!')
} }
private async onmessage(event: MessageEvent): Promise<void> { private async onmessage (event: MessageEvent): Promise<void> {
let data = event.data let data = event.data
if (data instanceof ArrayBuffer) { if (data instanceof ArrayBuffer) {
data = new Uint8Array(data) data = new Uint8Array(data)
@ -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) {
@ -184,12 +184,12 @@ class Gateway {
} }
} }
private onerror(event: Event | ErrorEvent): void { private onerror (event: Event | ErrorEvent): void {
const eventError = event as ErrorEvent const eventError = event as ErrorEvent
console.log(eventError) console.log(eventError)
} }
private async sendIdentify(forceNewSession?: boolean): Promise<void> { private async sendIdentify (forceNewSession?: boolean): Promise<void> {
if (this.client.bot === true) { if (this.client.bot === true) {
this.debug('Fetching /gateway/bot...') this.debug('Fetching /gateway/bot...')
const info = await this.client.rest.get(GATEWAY_BOT()) const info = await this.client.rest.get(GATEWAY_BOT())
@ -219,8 +219,8 @@ class Gateway {
token: this.token, token: this.token,
properties: { properties: {
$os: Deno.build.os, $os: Deno.build.os,
$browser: 'discord.deno', // TODO: Change lib name $browser: 'harmony', // TODO: Change lib name
$device: 'discord.deno' $device: 'harmony'
}, },
compress: true, compress: true,
shard: [0, 1], // TODO: Make sharding possible shard: [0, 1], // TODO: Make sharding possible
@ -234,23 +234,23 @@ class Gateway {
if (this.client.bot === false) { if (this.client.bot === false) {
// TODO: Complete Selfbot support // TODO: Complete Selfbot support
this.debug("Modify Identify Payload for Self-bot..") this.debug('Modify Identify Payload for Self-bot..')
// delete payload.d['intents'] // delete payload.d['intents']
// payload.d.intents = Intents.None // payload.d.intents = Intents.None
payload.d.presence = null payload.d.presence = null
payload.d.properties = { payload.d.properties = {
$os: "Windows", $os: 'Windows',
$browser: "Firefox", $browser: 'Firefox',
$device: "" $device: ''
} }
this.debug("Warn: Support for selfbots is incomplete") this.debug('Warn: Support for selfbots is incomplete')
} }
this.send(payload) this.send(payload)
} }
private async sendResume(): Promise<void> { private async sendResume (): Promise<void> {
this.debug(`Preparing to resume with Session: ${this.sessionID}`) this.debug(`Preparing to resume with Session: ${this.sessionID}`)
if (this.sequenceID === undefined) { if (this.sequenceID === undefined) {
const cached = await this.cache.get('seq') const cached = await this.cache.get('seq')
@ -268,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')
@ -280,7 +280,7 @@ class Gateway {
this.initWebsocket() this.initWebsocket()
} }
initWebsocket(): void { initWebsocket (): void {
this.websocket = new WebSocket( this.websocket = new WebSocket(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`, `${DISCORD_GATEWAY_URL}/?v=${DISCORD_API_VERSION}&encoding=json`,
@ -293,39 +293,41 @@ class Gateway {
this.websocket.onerror = this.onerror.bind(this) this.websocket.onerror = this.onerror.bind(this)
} }
close(): void { close (): void {
this.websocket.close(1000) this.websocket.close(1000)
} }
send(data: GatewayResponse): boolean { send (data: GatewayResponse): boolean {
if (this.websocket.readyState !== this.websocket.OPEN) return false if (this.websocket.readyState !== this.websocket.OPEN) return false
this.websocket.send(JSON.stringify({ this.websocket.send(
JSON.stringify({
op: data.op, op: data.op,
d: data.d, d: data.d,
s: typeof data.s === "number" ? data.s : null, s: typeof data.s === 'number' ? data.s : null,
t: data.t === undefined ? null : data.t, t: data.t === undefined ? null : data.t
})) })
)
return true return true
} }
sendPresence(data: ClientActivityPayload): void { sendPresence (data: ClientActivityPayload): void {
this.send({ this.send({
op: GatewayOpcodes.PRESENCE_UPDATE, op: GatewayOpcodes.PRESENCE_UPDATE,
d: data d: data
}) })
} }
sendHeartbeat(): void { sendHeartbeat (): void {
const payload = { const payload = {
op: GatewayOpcodes.HEARTBEAT, op: GatewayOpcodes.HEARTBEAT,
d: this.sequenceID ?? null d: this.sequenceID ?? null
}; }
this.send(payload) this.send(payload)
this.lastPingTimestamp = Date.now() this.lastPingTimestamp = Date.now()
} }
heartbeat(): void { heartbeat (): void {
if (this.heartbeatServerResponded) { if (this.heartbeatServerResponded) {
this.heartbeatServerResponded = false this.heartbeatServerResponded = false
} else { } else {

View file

@ -1,18 +1,22 @@
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
import { Emoji } from '../structures/emoji.ts' import { Emoji } from '../structures/emoji.ts'
import { Guild } from '../structures/guild.ts'
import { EmojiPayload } from '../types/emoji.ts' import { EmojiPayload } from '../types/emoji.ts'
import { CHANNEL } from '../types/endpoint.ts' import { GUILD_EMOJI } from '../types/endpoint.ts'
import { BaseManager } from './base.ts' import { BaseManager } from './base.ts'
export class EmojisManager extends BaseManager<EmojiPayload, Emoji> { export class EmojisManager extends BaseManager<EmojiPayload, Emoji> {
constructor (client: Client) { guild: Guild
super(client, 'emojis', Emoji)
constructor (client: Client, guild: Guild) {
super(client, `emojis:${guild.id}`, Emoji)
this.guild = guild
} }
async fetch (id: string): Promise<Emoji> { async fetch (id: string): Promise<Emoji> {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
this.client.rest this.client.rest
.get(CHANNEL(id)) .get(GUILD_EMOJI(this.guild.id, id))
.then(data => { .then(data => {
this.set(id, data as EmojiPayload) this.set(id, data as EmojiPayload)
resolve(new Emoji(this.client, data as EmojiPayload)) resolve(new Emoji(this.client, data as EmojiPayload))

View file

@ -13,18 +13,24 @@ import { CHANNEL } from '../types/endpoint.ts'
import { BaseChildManager } from './baseChild.ts' import { BaseChildManager } from './baseChild.ts'
import { ChannelsManager } from './channels.ts' import { ChannelsManager } from './channels.ts'
export type GuildChannelPayloads = GuildTextChannelPayload | GuildVoiceChannelPayload | GuildChannelCategoryPayload export type GuildChannelPayloads =
| GuildTextChannelPayload
| GuildVoiceChannelPayload
| GuildChannelCategoryPayload
export type GuildChannel = GuildTextChannel | VoiceChannel | CategoryChannel export type GuildChannel = GuildTextChannel | VoiceChannel | CategoryChannel
export class GuildChannelsManager extends BaseChildManager<GuildChannelPayloads, GuildChannel> { export class GuildChannelsManager extends BaseChildManager<
GuildChannelPayloads,
GuildChannel
> {
guild: Guild guild: Guild
constructor(client: Client, parent: ChannelsManager, guild: Guild) { constructor (client: Client, parent: ChannelsManager, guild: Guild) {
super(client, parent as any) super(client, parent as any)
this.guild = guild this.guild = guild
} }
async get(id: string): Promise<GuildChannel | undefined> { async get (id: string): Promise<GuildChannel | undefined> {
const res = await this.parent.get(id) const res = await this.parent.get(id)
if (res !== undefined && res.guild.id === this.guild.id) return res if (res !== undefined && res.guild.id === this.guild.id) return res
else return undefined else return undefined
@ -34,12 +40,14 @@ export class GuildChannelsManager extends BaseChildManager<GuildChannelPayloads,
return this.client.rest.delete(CHANNEL(id)) return this.client.rest.delete(CHANNEL(id))
} }
async array(): Promise<GuildChannel[]> { async array (): Promise<GuildChannel[]> {
const arr = await this.parent.array() as Channel[] const arr = (await this.parent.array()) as Channel[]
return arr.filter((c: any) => c.guild !== undefined && c.guild.id === this.guild.id) as any return arr.filter(
(c: any) => c.guild !== undefined && c.guild.id === this.guild.id
) as any
} }
async flush(): Promise<boolean> { async flush (): Promise<boolean> {
const arr = await this.array() const arr = await this.array()
for (const elem of arr) { for (const elem of arr) {
this.parent.delete(elem.id) this.parent.delete(elem.id)

View file

@ -10,18 +10,26 @@ export class GuildManager extends BaseManager<GuildPayload, Guild> {
super(client, 'guilds', Guild) super(client, 'guilds', Guild)
} }
async fetch(id: string): Promise<Guild> { async fetch (id: string): Promise<Guild> {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
this.client.rest.get(GUILD(id)).then(async (data: any) => { this.client.rest
.get(GUILD(id))
.then(async (data: any) => {
this.set(id, data) this.set(id, data)
const guild = new Guild(this.client, data) const guild = new Guild(this.client, data)
if ((data as GuildPayload).members !== undefined) { if ((data as GuildPayload).members !== undefined) {
const members = new MembersManager(this.client, guild) const members = new MembersManager(this.client, guild)
await members.fromPayload((data as GuildPayload).members as MemberPayload[]) await members.fromPayload(
(data as GuildPayload).members as MemberPayload[]
)
guild.members = members guild.members = members
} }
resolve(guild) resolve(guild)
}).catch(e => reject(e)) })
.catch(e => reject(e))
}) })
} }
} }

View file

@ -12,29 +12,53 @@ export class MessagesManager extends BaseManager<MessagePayload, Message> {
super(client, 'messages', Message) super(client, 'messages', Message)
} }
async get(key: string): Promise<Message | undefined> { async get (key: string): Promise<Message | undefined> {
const raw = await this._get(key) const raw = await this._get(key)
if (raw === undefined) return if (raw === undefined) return
let channel = await this.client.channels.get(raw.channel_id) let channel = await this.client.channels.get(raw.channel_id)
if (channel === undefined) channel = await this.client.channels.fetch(raw.channel_id) if (channel === undefined)
if (channel === undefined) return channel = await this.client.channels.fetch(raw.channel_id)
const author = new User(this.client, raw.author) const author = new User(this.client, raw.author)
const mentions = new MessageMentions() const mentions = new MessageMentions()
return new this.DataType(this.client, raw, channel, author, mentions) as any return new this.DataType(this.client, raw, channel, author, mentions) as any
} }
async fetch(channelID: string, id: string): Promise<Message> { async fetch (channelID: string, id: string): Promise<Message> {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
this.client.rest.get(CHANNEL_MESSAGE(channelID, id)).then(async data => { this.client.rest
.get(CHANNEL_MESSAGE(channelID, id))
.then(async data => {
this.set(id, data as MessagePayload) this.set(id, data as MessagePayload)
let channel: any = await this.client.channels.get<TextChannel>(channelID)
if (channel === undefined) channel = await this.client.channels.fetch(channelID) let channel: any = await this.client.channels.get<TextChannel>(
channelID
)
if (channel === undefined)
channel = await this.client.channels.fetch(channelID)
const author = new User(this.client, (data as MessagePayload).author) const author = new User(this.client, (data as MessagePayload).author)
await this.client.users.set(author.id, (data as MessagePayload).author) await this.client.users.set(
author.id,
(data as MessagePayload).author
)
// TODO: Make this thing work (MessageMentions) // TODO: Make this thing work (MessageMentions)
const mentions = new MessageMentions() const mentions = new MessageMentions()
resolve(new Message(this.client, data as MessagePayload, channel as TextChannel, author, mentions))
}).catch(e => reject(e)) resolve(
new Message(
this.client,
data as MessagePayload,
channel as TextChannel,
author,
mentions
)
)
})
.catch(e => reject(e))
}) })
} }
} }

View file

@ -73,7 +73,11 @@ export class Client extends EventEmitter {
this.intents = options.intents this.intents = options.intents
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.bot === false) this.bot = false
if (options.canary === true) this.canary = true if (options.canary === true) this.canary = true
} }
@ -94,7 +98,7 @@ export class Client extends EventEmitter {
/** Emit debug event */ /** Emit debug event */
debug (tag: string, msg: string): void { debug (tag: string, msg: string): void {
this.emit("debug", `[${tag}] ${msg}`) this.emit('debug', `[${tag}] ${msg}`)
} }
/** /**

View file

@ -29,7 +29,7 @@ export interface CommandTexts {
export const DefaultCommandTexts: CommandTexts = { export const DefaultCommandTexts: CommandTexts = {
GUILD_ONLY: 'This command can only be used in a Server!', GUILD_ONLY: 'This command can only be used in a Server!',
OWNER_ONLY: 'This command can only be used by Bot Owners!', OWNER_ONLY: 'This command can only be used by Bot Owners!',
DMS_ONLY: 'This command can only be used in Bot\'s DMs!', DMS_ONLY: "This command can only be used in Bot's DMs!",
ERROR: 'An error occured while executing command!' ERROR: 'An error occured while executing command!'
} }
@ -44,7 +44,7 @@ export const massReplace = (text: string, replaces: Replaces): string => {
return text return text
} }
export class CommandClient extends Client { export class CommandClient extends Client implements CommandClientOptions {
prefix: string | string[] prefix: string | string[]
mentionPrefix: boolean mentionPrefix: boolean
getGuildPrefix: (guildID: string) => PrefixReturnType getGuildPrefix: (guildID: string) => PrefixReturnType
@ -58,23 +58,38 @@ export class CommandClient extends Client {
commands: CommandsManager = new CommandsManager(this) commands: CommandsManager = new CommandsManager(this)
texts: CommandTexts = DefaultCommandTexts texts: CommandTexts = DefaultCommandTexts
constructor(options: CommandClientOptions) { constructor (options: CommandClientOptions) {
super(options) super(options)
this.prefix = options.prefix this.prefix = options.prefix
this.mentionPrefix = options.mentionPrefix === undefined ? false : options.mentionPrefix this.mentionPrefix =
this.getGuildPrefix = options.getGuildPrefix === undefined ? (id: string) => this.prefix : options.getGuildPrefix options.mentionPrefix === undefined ? false : options.mentionPrefix
this.getUserPrefix = options.getUserPrefix === undefined ? (id: string) => this.prefix : options.getUserPrefix this.getGuildPrefix =
this.spacesAfterPrefix = options.spacesAfterPrefix === undefined ? false : options.spacesAfterPrefix options.getGuildPrefix === undefined
this.betterArgs = options.betterArgs === undefined ? false : options.betterArgs ? (id: string) => this.prefix
: options.getGuildPrefix
this.getUserPrefix =
options.getUserPrefix === undefined
? (id: string) => this.prefix
: options.getUserPrefix
this.spacesAfterPrefix =
options.spacesAfterPrefix === undefined
? false
: options.spacesAfterPrefix
this.betterArgs =
options.betterArgs === undefined ? false : options.betterArgs
this.owners = options.owners === undefined ? [] : options.owners this.owners = options.owners === undefined ? [] : options.owners
this.allowBots = options.allowBots === undefined ? false : options.allowBots this.allowBots = options.allowBots === undefined ? false : options.allowBots
this.allowDMs = options.allowDMs === undefined ? true : options.allowDMs this.allowDMs = options.allowDMs === undefined ? true : options.allowDMs
this.caseSensitive = options.caseSensitive === undefined ? false : options.caseSensitive this.caseSensitive =
options.caseSensitive === undefined ? false : options.caseSensitive
this.on('messageCreate', async (msg: Message) => await this.processMessage(msg)) this.on(
'messageCreate',
async (msg: Message) => await this.processMessage(msg)
)
} }
async processMessage(msg: Message): Promise<any> { async processMessage (msg: Message): Promise<any> {
if (!this.allowBots && msg.author.bot === true) return if (!this.allowBots && msg.author.bot === true) return
let prefix: string | string[] = this.prefix let prefix: string | string[] = this.prefix
@ -113,15 +128,18 @@ export class CommandClient extends Client {
} }
if (command.guildOnly === true && msg.guild === undefined) { if (command.guildOnly === true && msg.guild === undefined) {
if (this.texts.GUILD_ONLY !== undefined) return this.sendProcessedText(msg, this.texts.GUILD_ONLY, baseReplaces) if (this.texts.GUILD_ONLY !== undefined)
return this.sendProcessedText(msg, this.texts.GUILD_ONLY, baseReplaces)
return return
} }
if (command.dmOnly === true && msg.guild !== undefined) { if (command.dmOnly === true && msg.guild !== undefined) {
if (this.texts.DMS_ONLY !== undefined) return this.sendProcessedText(msg, this.texts.DMS_ONLY, baseReplaces) if (this.texts.DMS_ONLY !== undefined)
return this.sendProcessedText(msg, this.texts.DMS_ONLY, baseReplaces)
return return
} }
if (command.ownerOnly === true && !this.owners.includes(msg.author.id)) { if (command.ownerOnly === true && !this.owners.includes(msg.author.id)) {
if (this.texts.OWNER_ONLY !== undefined) return this.sendProcessedText(msg, this.texts.OWNER_ONLY, baseReplaces) if (this.texts.OWNER_ONLY !== undefined)
return this.sendProcessedText(msg, this.texts.OWNER_ONLY, baseReplaces)
return return
} }
@ -151,15 +169,19 @@ export class CommandClient extends Client {
} }
} }
sendProcessedText(msg: Message, text: CommandText, replaces: Replaces): any { sendProcessedText (msg: Message, text: CommandText, replaces: Replaces): any {
if (typeof text === "string") { if (typeof text === 'string') {
text = massReplace(text, replaces) text = massReplace(text, replaces)
return msg.channel.send(text) return msg.channel.send(text)
} else { } else {
if (text.description !== undefined) text.description = massReplace(text.description, replaces) if (text.description !== undefined)
if (text.title !== undefined) text.description = massReplace(text.title, replaces) text.description = massReplace(text.description, replaces)
if (text.author?.name !== undefined) text.description = massReplace(text.author.name, replaces) if (text.title !== undefined)
if (text.footer?.text !== undefined) text.description = massReplace(text.footer.text, replaces) text.description = massReplace(text.title, replaces)
if (text.author?.name !== undefined)
text.description = massReplace(text.author.name, replaces)
if (text.footer?.text !== undefined)
text.description = massReplace(text.footer.text, replaces)
return msg.channel.send(text) return msg.channel.send(text)
} }
} }

View file

@ -156,7 +156,7 @@ export class RESTManager {
): { [key: string]: any } { ): { [key: string]: any } {
const headers: { [key: string]: string } = { const headers: { [key: string]: string } = {
Authorization: `Bot ${this.client.token}`, Authorization: `Bot ${this.client.token}`,
'User-Agent': `DiscordBot (discord.deno)` 'User-Agent': `DiscordBot (harmony)`
} }
if (this.client.token === undefined) delete headers.Authorization if (this.client.token === undefined) delete headers.Authorization
@ -190,7 +190,9 @@ export class RESTManager {
data.headers['sec-fetch-dest'] = 'empty' data.headers['sec-fetch-dest'] = 'empty'
data.headers['sec-fetch-mode'] = 'cors' data.headers['sec-fetch-mode'] = 'cors'
data.headers['sec-fetch-site'] = 'same-origin' data.headers['sec-fetch-site'] = 'same-origin'
data.headers['x-super-properties'] = btoa(JSON.stringify(getBuildInfo(this.client))) data.headers['x-super-properties'] = btoa(
JSON.stringify(getBuildInfo(this.client))
)
delete data.headers['User-Agent'] delete data.headers['User-Agent']
delete data.headers.Authorization delete data.headers.Authorization
headers.credentials = 'include' headers.credentials = 'include'
@ -259,10 +261,7 @@ export class RESTManager {
const requestData = this.createRequestBody(body, method) const requestData = this.createRequestBody(body, method)
const response = await fetch( const response = await fetch(urlToUse, requestData)
urlToUse,
requestData
)
const bucketIDFromHeaders = this.processHeaders(url, response.headers) const bucketIDFromHeaders = this.processHeaders(url, response.headers)
this.handleStatusCode(response, errorStack) this.handleStatusCode(response, errorStack)
@ -328,7 +327,8 @@ export class RESTManager {
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
this.logErrors(response, errorStack) this.logErrors(response, errorStack)
if (status === HttpResponseCode.Unauthorized) throw new Error("Request was not successful. Invalid Token.") if (status === HttpResponseCode.Unauthorized)
throw new Error('Request was not successful. Invalid Token.')
switch (status) { switch (status) {
case HttpResponseCode.BadRequest: case HttpResponseCode.BadRequest:

View file

@ -6,7 +6,7 @@ import { VoiceState } from './voiceState.ts'
import { RolesManager } from '../managers/roles.ts' import { RolesManager } from '../managers/roles.ts'
import { GuildChannelsManager } from '../managers/guildChannels.ts' import { GuildChannelsManager } from '../managers/guildChannels.ts'
import { MembersManager } from '../managers/members.ts' import { MembersManager } from '../managers/members.ts'
import { EmojisManager } from "../managers/emojis.ts" import { Emoji } from "./emoji.ts"
export class Guild extends Base { export class Guild extends Base {
id: string id: string
@ -27,7 +27,7 @@ export class Guild extends Base {
defaultMessageNotifications?: string defaultMessageNotifications?: string
explicitContentFilter?: string explicitContentFilter?: string
roles: RolesManager roles: RolesManager
emojis: EmojisManager emojis: Emoji[] = []
features?: GuildFeatures[] features?: GuildFeatures[]
mfaLevel?: string mfaLevel?: string
applicationID?: string applicationID?: string

View file

@ -122,8 +122,8 @@ export enum UpdateStatus {
export interface IdentityConnection { export interface IdentityConnection {
$os: 'darwin' | 'windows' | 'linux' | 'custom os' $os: 'darwin' | 'windows' | 'linux' | 'custom os'
$browser: 'discord.deno' $browser: 'harmony'
$device: 'discord.deno' $device: 'harmony'
} }
export interface Resume { export interface Resume {

View file

@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { Client } from '../models/client.ts' import { Client } from '../models/client.ts'
export const getBuildInfo = (client: Client): { export const getBuildInfo = (
client: Client
): {
os: string os: string
os_version: string os_version: string
browser: string browser: string
@ -11,8 +13,8 @@ export const getBuildInfo = (client: Client): {
client_event_source: null client_event_source: null
release_channel: string release_channel: string
} => { } => {
const os = 'Windows' let os = 'Windows'
const os_version = '10' let os_version = '10'
let client_build_number = 71073 let client_build_number = 71073
const client_event_source = null const client_event_source = null
let release_channel = 'stable' let release_channel = 'stable'
@ -20,11 +22,22 @@ export const getBuildInfo = (client: Client): {
release_channel = 'canary' release_channel = 'canary'
client_build_number = 71076 client_build_number = 71076
} }
const browser = 'Firefox' let browser = 'Firefox'
const browser_version = '83.0' let browser_version = '83.0'
const browser_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 ' + browser + '/' + browser_version 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 // TODO: Use current OS properties, but also browser_user_agent accordingly
// if (Deno.build.os === 'darwin') os = 'MacOS' if (Deno.build.os === 'darwin') {
os = 'MacOS'
os_version = '10.15.6'
browser = 'Safari'
browser_version = '14.0.1'
browser_user_agent =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15'
}
// else if (Deno.build.os === 'linux') os = 'Ubuntu' // else if (Deno.build.os === 'linux') os = 'Ubuntu'
return { return {
@ -35,6 +48,6 @@ export const getBuildInfo = (client: Client): {
browser_user_agent, browser_user_agent,
client_build_number, client_build_number,
client_event_source, client_event_source,
release_channel, release_channel
} }
}; }

View file

@ -18,55 +18,19 @@ export class Intents {
GatewayIntents.GUILD_MESSAGE_TYPING, GatewayIntents.GUILD_MESSAGE_TYPING,
GatewayIntents.GUILD_VOICE_STATES, GatewayIntents.GUILD_VOICE_STATES,
GatewayIntents.GUILD_WEBHOOKS GatewayIntents.GUILD_WEBHOOKS
]; ]
static Presence: number[] = [ static Presence: number[] = [
GatewayIntents.GUILD_PRESENCES, GatewayIntents.GUILD_PRESENCES,
GatewayIntents.GUILD_MESSAGES, GatewayIntents.GUILDS
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
];
static GuildMembers: number[] = [ static GuildMembers: number[] = [
GatewayIntents.GUILD_MEMBERS, GatewayIntents.GUILD_MEMBERS,
GatewayIntents.GUILD_MESSAGES,
GatewayIntents.DIRECT_MESSAGES,
GatewayIntents.DIRECT_MESSAGE_REACTIONS,
GatewayIntents.DIRECT_MESSAGE_TYPING,
GatewayIntents.GUILDS, GatewayIntents.GUILDS,
GatewayIntents.GUILD_BANS, GatewayIntents.GUILD_BANS,
GatewayIntents.GUILD_EMOJIS, GatewayIntents.GUILD_VOICE_STATES
GatewayIntents.GUILD_INTEGRATIONS,
GatewayIntents.GUILD_INVITES,
GatewayIntents.GUILD_MESSAGE_REACTIONS,
GatewayIntents.GUILD_MESSAGE_TYPING,
GatewayIntents.GUILD_VOICE_STATES,
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
] ]
static None: number[] = []
} }