new things

This commit is contained in:
DjDeveloperr 2021-02-12 17:07:38 +05:30
parent f4b4c640b3
commit a800f394ac
9 changed files with 219 additions and 130 deletions

View File

@ -1,7 +1,16 @@
import { Guild } from '../../structures/guild.ts'
import { Member } from '../../structures/member.ts'
import { Interaction } from '../../structures/slash.ts'
import { Role } from '../../structures/role.ts'
import {
Interaction,
InteractionApplicationCommandResolved,
InteractionChannel
} from '../../structures/slash.ts'
import { GuildTextChannel } from '../../structures/textChannel.ts'
import { User } from '../../structures/user.ts'
import { InteractionPayload } from '../../types/slash.ts'
import { UserPayload } from '../../types/user.ts'
import { Permissions } from '../../utils/permissions.ts'
import { Gateway, GatewayEventHandler } from '../index.ts'
export const interactionCreate: GatewayEventHandler = async (
@ -18,13 +27,12 @@ export const interactionCreate: GatewayEventHandler = async (
d.guild_id === undefined
? undefined
: await gateway.client.guilds.get(d.guild_id)
if (guild === undefined) return
if (d.member !== undefined)
await guild.members.set(d.member.user.id, d.member)
await guild?.members.set(d.member.user.id, d.member)
const member =
d.member !== undefined
? (((await guild.members.get(d.member.user.id)) as unknown) as Member)
? (((await guild?.members.get(d.member.user.id)) as unknown) as Member)
: undefined
if (d.user !== undefined) await gateway.client.users.set(d.user.id, d.user)
const dmUser =
@ -37,11 +45,66 @@ export const interactionCreate: GatewayEventHandler = async (
(await gateway.client.channels.get<GuildTextChannel>(d.channel_id)) ??
(await gateway.client.channels.fetch<GuildTextChannel>(d.channel_id))
const resolved: InteractionApplicationCommandResolved = {
users: {},
channels: {},
members: {},
roles: {}
}
if (d.data?.resolved !== undefined) {
for (const [id, data] of Object.entries(d.data.resolved.users ?? {})) {
await gateway.client.users.set(id, data)
resolved.users[id] = ((await gateway.client.users.get(
id
)) as unknown) as User
if (resolved.members[id] !== undefined)
resolved.users[id].member = resolved.members[id]
}
for (const [id, data] of Object.entries(d.data.resolved.members ?? {})) {
const roles = await guild?.roles.array()
let permissions = new Permissions(Permissions.DEFAULT)
if (roles !== undefined) {
const mRoles = roles.filter(
(r) => (data?.roles?.includes(r.id) as boolean) || r.id === guild?.id
)
permissions = new Permissions(mRoles.map((r) => r.permissions))
}
data.user = (d.data.resolved.users?.[id] as unknown) as UserPayload
resolved.members[id] = new Member(
gateway.client,
data,
resolved.users[id],
guild as Guild,
permissions
)
}
for (const [id, data] of Object.entries(d.data.resolved.roles ?? {})) {
if (guild !== undefined) {
await guild.roles.set(id, data)
resolved.roles[id] = ((await guild.roles.get(id)) as unknown) as Role
} else {
resolved.roles[id] = new Role(
gateway.client,
data,
(guild as unknown) as Guild
)
}
}
for (const [id, data] of Object.entries(d.data.resolved.channels ?? {})) {
resolved.channels[id] = new InteractionChannel(gateway.client, data)
}
}
const interaction = new Interaction(gateway.client, d, {
member,
guild,
channel,
user
user,
resolved
})
gateway.client.emit('interactionCreate', interaction)
}

View File

@ -7,7 +7,6 @@ import {
import { GatewayResponse } from '../types/gatewayResponse.ts'
import {
GatewayOpcodes,
GatewayIntents,
GatewayCloseCodes,
IdentityPayload,
StatusUpdatePayload,
@ -57,8 +56,6 @@ export type GatewayTypedEvents = {
*/
export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
websocket?: WebSocket
token?: string
intents?: GatewayIntents[]
connected = false
initialized = false
heartbeatInterval = 0
@ -269,8 +266,9 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
}
private async sendIdentify(forceNewSession?: boolean): Promise<void> {
if (typeof this.token !== 'string') throw new Error('Token not specified')
if (typeof this.intents !== 'object')
if (typeof this.client.token !== 'string')
throw new Error('Token not specified')
if (typeof this.client.intents !== 'object')
throw new Error('Intents not specified')
if (this.client.fetchGatewayInfo === true) {
@ -300,7 +298,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
}
const payload: IdentityPayload = {
token: this.token,
token: this.client.token,
properties: {
$os: this.client.clientProperties.os ?? Deno.build.os,
$browser: this.client.clientProperties.browser ?? 'harmony',
@ -311,7 +309,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
this.shards === undefined
? [0, 1]
: [this.shards[0] ?? 0, this.shards[1] ?? 1],
intents: this.intents.reduce(
intents: this.client.intents.reduce(
(previous, current) => previous | current,
0
),
@ -327,9 +325,8 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
}
private async sendResume(): Promise<void> {
if (typeof this.token !== 'string') throw new Error('Token not specified')
if (typeof this.intents !== 'object')
throw new Error('Intents not specified')
if (typeof this.client.token !== 'string')
throw new Error('Token not specified')
if (this.sessionID === undefined) {
this.sessionID = await this.cache.get(
@ -348,7 +345,7 @@ export class Gateway extends HarmonyEventEmitter<GatewayTypedEvents> {
const resumePayload = {
op: GatewayOpcodes.RESUME,
d: {
token: this.token,
token: this.client.token,
session_id: this.sessionID,
seq: this.sequenceID ?? null
}

View File

@ -79,8 +79,6 @@ export class ShardManager extends HarmonyEventEmitter<ShardManagerEvents> {
const shardCount = await this.getShardCount()
const gw = new Gateway(this.client, [Number(id), shardCount])
gw.token = this.client.token
gw.intents = this.client.intents
this.list.set(id.toString(), gw)
gw.initWebsocket()

View File

@ -2,10 +2,10 @@ import { Client } from '../models/client.ts'
import { Snowflake } from '../utils/snowflake.ts'
export class Base {
client: Client
client!: Client
constructor(client: Client, _data?: any) {
this.client = client
Object.defineProperty(this, 'client', { value: client, enumerable: false })
}
}

View File

@ -1,24 +1,31 @@
import { Client } from '../models/client.ts'
import {
AllowedMentionsPayload,
ChannelTypes,
EmbedPayload,
MessageOptions
} from '../types/channel.ts'
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
import {
Dict,
InteractionApplicationCommandData,
InteractionApplicationCommandOption,
InteractionChannelPayload,
InteractionPayload,
InteractionResponseFlags,
InteractionResponsePayload,
InteractionResponseType,
InteractionType
InteractionType,
SlashCommandOptionType
} from '../types/slash.ts'
import { Permissions } from '../utils/permissions.ts'
import { SnowflakeBase } from './base.ts'
import { Channel } from './channel.ts'
import { Embed } from './embed.ts'
import { Guild } from './guild.ts'
import { Member } from './member.ts'
import { Message } from './message.ts'
import { Role } from './role.ts'
import { GuildTextChannel, TextChannel } from './textChannel.ts'
import { User } from './user.ts'
@ -54,8 +61,37 @@ export interface InteractionResponse extends InteractionMessageOptions {
ephemeral?: boolean
}
/** Represents a Channel Object for an Option in Slash Command */
export class InteractionChannel extends SnowflakeBase {
name: string
type: ChannelTypes
permissions: Permissions
constructor(client: Client, data: InteractionChannelPayload) {
super(client)
this.name = data.name
this.type = data.type
this.permissions = new Permissions(data.permissions)
}
/** Resolve to actual Channel object if present in Cache */
async resolve<T = Channel>(): Promise<T | undefined> {
return this.client.channels.get<T>(this.id)
}
}
export interface InteractionApplicationCommandResolved {
users: Dict<InteractionUser>
members: Dict<Member>
channels: Dict<InteractionChannel>
roles: Dict<Role>
}
export class InteractionUser extends User {
member?: Member
}
export class Interaction extends SnowflakeBase {
client: Client
/** Type of Interaction */
type: InteractionType
/** Interaction Token */
@ -72,6 +108,7 @@ export class Interaction extends SnowflakeBase {
/** User object of who invoked Interaction */
user: User
responded: boolean = false
resolved: InteractionApplicationCommandResolved
constructor(
client: Client,
@ -81,10 +118,10 @@ export class Interaction extends SnowflakeBase {
guild?: Guild
member?: Member
user: User
resolved: InteractionApplicationCommandResolved
}
) {
super(client)
this.client = client
this.type = data.type
this.token = data.token
this.member = others.member
@ -93,6 +130,7 @@ export class Interaction extends SnowflakeBase {
this.data = data.data
this.guild = others.guild
this.channel = others.channel
this.resolved = others.resolved
}
/** Name of the Command Used (may change with future additions to Interactions!) */
@ -105,8 +143,16 @@ export class Interaction extends SnowflakeBase {
}
/** Get an option by name */
option<T = any>(name: string): T {
return this.options.find((e) => e.name === name)?.value
option<T>(name: string): T {
const op = this.options.find((e) => e.name === name)
if (op === undefined || op.value === undefined) return undefined as any
if (op.type === SlashCommandOptionType.USER)
return this.resolved.users[op.value] as any
else if (op.type === SlashCommandOptionType.ROLE)
return this.resolved.roles[op.value] as any
else if (op.type === SlashCommandOptionType.CHANNEL)
return this.resolved.channels[op.value] as any
else return op.value
}
/** Respond to an Interaction */

View File

@ -1,100 +1,56 @@
import { Client, Intents, event, slash } from '../../mod.ts'
import { Embed } from '../structures/embed.ts'
import {
Client,
Intents,
event,
slash,
SlashCommandOptionType as Type
} from '../../mod.ts'
import { Interaction } from '../structures/slash.ts'
import { TOKEN } from './config.ts'
export class MyClient extends Client {
@event()
ready(): void {
@event() ready(): void {
console.log(`Logged in as ${this.user?.tag}!`)
this.slash.commands.bulkEdit([{ name: 'send', description: 'idk' }])
}
@event('debug')
debugEvt(txt: string): void {
console.log(txt)
}
@slash()
send(d: Interaction): void {
d.respond({
content: d.data.options?.find((e) => e.name === 'content')?.value
})
}
@slash()
async eval(d: Interaction): Promise<void> {
if (
d.user.id !== '422957901716652033' &&
d.user.id !== '682849186227552266'
) {
d.respond({
content: 'This command can only be used by owner!'
})
} else {
const code = d.data.options?.find((e) => e.name === 'code')
?.value as string
try {
// eslint-disable-next-line no-eval
let evaled = eval(code)
if (evaled instanceof Promise) evaled = await evaled
if (typeof evaled === 'object') evaled = Deno.inspect(evaled)
let res = `${evaled}`.substring(0, 1990)
while (client.token !== undefined && res.includes(client.token)) {
res = res.replace(client.token, '[REMOVED]')
this.slash.commands.bulkEdit(
[
{
name: 'test',
description: 'Test command.',
options: [
{
name: 'user',
type: Type.USER,
description: 'User'
},
{
name: 'role',
type: Type.ROLE,
description: 'Role'
},
{
name: 'channel',
type: Type.CHANNEL,
description: 'Channel'
},
{
name: 'string',
type: Type.STRING,
description: 'String'
}
]
}
d.respond({
content: '```js\n' + `${res}` + '\n```'
}).catch(() => {})
} catch (e) {
d.respond({
content: '```js\n' + `${e.stack}` + '\n```'
})
}
}
],
'807935370556866560'
)
this.slash.commands.bulkEdit([])
}
@slash()
async hug(d: Interaction): Promise<void> {
const id = d.data.options?.find((e) => e.name === 'user')?.value as string
const user = (await client.users.get(id)) ?? (await client.users.fetch(id))
const url = await fetch('https://nekos.life/api/v2/img/hug')
.then((r) => r.json())
.then((e) => e.url)
d.respond({
embeds: [
new Embed()
.setTitle(`${d.user.username} hugged ${user?.username}!`)
.setImage({ url })
.setColor(0x2f3136)
]
})
@slash() test(d: Interaction): void {
console.log(d.resolved)
}
@slash()
async kiss(d: Interaction): Promise<void> {
const id = d.data.options?.find((e) => e.name === 'user')?.value as string
const user = (await client.users.get(id)) ?? (await client.users.fetch(id))
const url = await fetch('https://nekos.life/api/v2/img/kiss')
.then((r) => r.json())
.then((e) => e.url)
d.respond({
embeds: [
new Embed()
.setTitle(`${d.user.username} kissed ${user?.username}!`)
.setImage({ url })
.setColor(0x2f3136)
]
})
}
@slash('ping')
pingCmd(d: Interaction): void {
d.respond({
content: `Pong!`
})
@event() raw(evt: string, d: any): void {
if (evt === 'INTERACTION_CREATE') console.log(evt, d?.data?.resolved)
}
}

View File

@ -1,5 +1,10 @@
import { AllowedMentionsPayload, EmbedPayload } from './channel.ts'
import {
AllowedMentionsPayload,
ChannelTypes,
EmbedPayload
} from './channel.ts'
import { MemberPayload } from './guild.ts'
import { RolePayload } from './role.ts'
import { UserPayload } from './user.ts'
export interface InteractionApplicationCommandOption {
@ -13,6 +18,24 @@ export interface InteractionApplicationCommandOption {
options?: InteractionApplicationCommandOption[]
}
export interface InteractionChannelPayload {
id: string
name: string
permissions: string
type: ChannelTypes
}
export interface Dict<T> {
[name: string]: T
}
export interface InteractionApplicationCommandResolvedPayload {
users?: Dict<UserPayload>
members?: Dict<MemberPayload>
channels?: Dict<InteractionChannelPayload>
roles?: Dict<RolePayload>
}
export interface InteractionApplicationCommandData {
/** Name of the Slash Command */
name: string
@ -20,6 +43,8 @@ export interface InteractionApplicationCommandData {
id: string
/** Options (arguments) sent with Interaction */
options: InteractionApplicationCommandOption[]
/** Resolved data for options in Slash Command */
resolved?: InteractionApplicationCommandResolvedPayload
}
export enum InteractionType {

View File

@ -9,31 +9,31 @@ export type BitFieldResolvable =
/** Bit Field utility to work with Bits and Flags */
export class BitField {
flags: { [name: string]: number } = {}
#flags: { [name: string]: number } = {}
bitfield: number
constructor(flags: { [name: string]: number }, bits: any) {
this.flags = flags
this.bitfield = BitField.resolve(this.flags, bits)
this.#flags = flags
this.bitfield = BitField.resolve(this.#flags, bits)
}
any(bit: BitFieldResolvable): boolean {
return (this.bitfield & BitField.resolve(this.flags, bit)) !== 0
return (this.bitfield & BitField.resolve(this.#flags, bit)) !== 0
}
equals(bit: BitFieldResolvable): boolean {
return this.bitfield === BitField.resolve(this.flags, bit)
return this.bitfield === BitField.resolve(this.#flags, bit)
}
has(bit: BitFieldResolvable, ...args: any[]): boolean {
if (Array.isArray(bit)) return (bit.every as any)((p: any) => this.has(p))
bit = BitField.resolve(this.flags, bit)
bit = BitField.resolve(this.#flags, bit)
return (this.bitfield & bit) === bit
}
missing(bits: any, ...hasParams: any[]): string[] {
if (!Array.isArray(bits))
bits = new BitField(this.flags, bits).toArray(false)
bits = new BitField(this.#flags, bits).toArray(false)
return bits.filter((p: any) => !this.has(p, ...hasParams))
}
@ -44,10 +44,10 @@ export class BitField {
add(...bits: BitFieldResolvable[]): BitField {
let total = 0
for (const bit of bits) {
total |= BitField.resolve(this.flags, bit)
total |= BitField.resolve(this.#flags, bit)
}
if (Object.isFrozen(this))
return new BitField(this.flags, this.bitfield | total)
return new BitField(this.#flags, this.bitfield | total)
this.bitfield |= total
return this
}
@ -55,27 +55,31 @@ export class BitField {
remove(...bits: BitFieldResolvable[]): BitField {
let total = 0
for (const bit of bits) {
total |= BitField.resolve(this.flags, bit)
total |= BitField.resolve(this.#flags, bit)
}
if (Object.isFrozen(this))
return new BitField(this.flags, this.bitfield & ~total)
return new BitField(this.#flags, this.bitfield & ~total)
this.bitfield &= ~total
return this
}
flags(): { [name: string]: number } {
return this.#flags
}
serialize(...hasParams: any[]): { [key: string]: any } {
const serialized: { [key: string]: any } = {}
for (const [flag, bit] of Object.entries(this.flags))
for (const [flag, bit] of Object.entries(this.#flags))
serialized[flag] = this.has(
BitField.resolve(this.flags, bit),
BitField.resolve(this.#flags, bit),
...hasParams
)
return serialized
}
toArray(...hasParams: any[]): string[] {
return Object.keys(this.flags).filter((bit) =>
this.has(BitField.resolve(this.flags, bit), ...hasParams)
return Object.keys(this.#flags).filter((bit) =>
this.has(BitField.resolve(this.#flags, bit), ...hasParams)
)
}

View File

@ -21,14 +21,14 @@ export class Permissions extends BitField {
any(permission: PermissionResolvable, checkAdmin = true): boolean {
return (
(checkAdmin && super.has(this.flags.ADMINISTRATOR)) ||
(checkAdmin && super.has(this.flags().ADMINISTRATOR)) ||
super.any(permission as any)
)
}
has(permission: PermissionResolvable, checkAdmin = true): boolean {
return (
(checkAdmin && super.has(this.flags.ADMINISTRATOR)) ||
(checkAdmin && super.has(this.flags().ADMINISTRATOR)) ||
super.has(permission as any)
)
}