@satorijs/adapter-discord 4.1.3 → 4.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -2
- package/lib/utils.d.ts +1 -1
- package/package.json +4 -3
- package/src/bot.ts +221 -0
- package/src/index.ts +27 -0
- package/src/message.ts +446 -0
- package/src/types/.eslintrc.yml +2 -0
- package/src/types/application-role-connection.ts +61 -0
- package/src/types/application.ts +120 -0
- package/src/types/audit-log.ts +219 -0
- package/src/types/auto-moderation.ts +189 -0
- package/src/types/ban.ts +92 -0
- package/src/types/channel.ts +501 -0
- package/src/types/command.ts +320 -0
- package/src/types/component.ts +125 -0
- package/src/types/device.ts +44 -0
- package/src/types/emoji.ts +96 -0
- package/src/types/gateway.ts +334 -0
- package/src/types/guild-member.ts +260 -0
- package/src/types/guild-template.ts +109 -0
- package/src/types/guild.ts +476 -0
- package/src/types/index.ts +49 -0
- package/src/types/integration.ts +130 -0
- package/src/types/interaction.ts +283 -0
- package/src/types/internal.ts +44 -0
- package/src/types/invite.ts +192 -0
- package/src/types/message.ts +470 -0
- package/src/types/presence.ts +163 -0
- package/src/types/reaction.ts +139 -0
- package/src/types/role.ts +252 -0
- package/src/types/scheduled-event.ts +200 -0
- package/src/types/stage-instance.ts +98 -0
- package/src/types/sticker.ts +179 -0
- package/src/types/team.ts +33 -0
- package/src/types/thread.ts +298 -0
- package/src/types/user.ts +154 -0
- package/src/types/voice.ts +124 -0
- package/src/types/webhook.ts +246 -0
- package/src/utils.ts +391 -0
- package/src/ws.ts +124 -0
package/lib/utils.d.ts
CHANGED
|
@@ -11,6 +11,6 @@ export declare const decodeRole: (role: Discord.Role) => Universal.GuildRole;
|
|
|
11
11
|
export declare const encodeRole: (role: Partial<Universal.GuildRole>) => Partial<Discord.Role>;
|
|
12
12
|
export declare function decodeMessage(bot: DiscordBot, data: Discord.Message, message: Universal.Message, payload?: Universal.MessageLike, details?: boolean): Promise<Universal.Message>;
|
|
13
13
|
export declare function setupMessageGuildId(session: Session, guildId: string): void;
|
|
14
|
-
export declare function adaptSession<C extends Context>(bot: DiscordBot<C>, input: Discord.Gateway.Payload): Promise<
|
|
14
|
+
export declare function adaptSession<C extends Context>(bot: DiscordBot<C>, input: Discord.Gateway.Payload): Promise<C[typeof Context.session]>;
|
|
15
15
|
export declare const encodeCommand: (cmd: Universal.Command) => Discord.ApplicationCommand.Params.Create;
|
|
16
16
|
export declare function encodeCommandOptions(cmd: Universal.Command): Discord.ApplicationCommand.Option[];
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@satorijs/adapter-discord",
|
|
3
3
|
"description": "Discord Adapter for Satorijs",
|
|
4
|
-
"version": "4.1.
|
|
4
|
+
"version": "4.1.4",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
|
-
"lib"
|
|
8
|
+
"lib",
|
|
9
|
+
"src"
|
|
9
10
|
],
|
|
10
11
|
"author": "LittleC <i@ltlec.cn>",
|
|
11
12
|
"contributors": [
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
"chat"
|
|
32
33
|
],
|
|
33
34
|
"peerDependencies": {
|
|
34
|
-
"@satorijs/satori": "^3.
|
|
35
|
+
"@satorijs/satori": "^3.1.2"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"form-data": "^4.0.0"
|
package/src/bot.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { Bot, Context, Fragment, h, Logger, Quester, Schema, Universal } from '@satorijs/satori'
|
|
2
|
+
import * as Discord from './utils'
|
|
3
|
+
import { DiscordMessageEncoder } from './message'
|
|
4
|
+
import { Internal, Webhook } from './types'
|
|
5
|
+
import { WsClient } from './ws'
|
|
6
|
+
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
import { version } from '../package.json'
|
|
9
|
+
|
|
10
|
+
const logger = new Logger('discord')
|
|
11
|
+
|
|
12
|
+
export class DiscordBot<C extends Context = Context> extends Bot<C, DiscordBot.Config> {
|
|
13
|
+
static MessageEncoder = DiscordMessageEncoder
|
|
14
|
+
|
|
15
|
+
public http: Quester
|
|
16
|
+
public internal: Internal
|
|
17
|
+
public webhooks: Record<string, Webhook> = {}
|
|
18
|
+
public webhookLock: Record<string, Promise<Webhook>> = {}
|
|
19
|
+
public commands: Universal.Command[] = []
|
|
20
|
+
|
|
21
|
+
constructor(ctx: C, config: DiscordBot.Config) {
|
|
22
|
+
super(ctx, config)
|
|
23
|
+
this.platform = 'discord'
|
|
24
|
+
this.http = ctx.http.extend({
|
|
25
|
+
...config,
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bot ${config.token}`,
|
|
28
|
+
'User-Agent': `Koishi (https://koishi.chat/, ${version})`,
|
|
29
|
+
...config.headers,
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
this.internal = new Internal(this.http)
|
|
33
|
+
ctx.plugin(WsClient, this)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async _ensureWebhook(channelId: string) {
|
|
37
|
+
let webhook: Webhook
|
|
38
|
+
const webhooks = await this.internal.getChannelWebhooks(channelId)
|
|
39
|
+
const selfId = this.selfId
|
|
40
|
+
if (!webhooks.find(v => v.name === 'Koishi' && v.user.id === selfId)) {
|
|
41
|
+
webhook = await this.internal.createWebhook(channelId, {
|
|
42
|
+
name: 'Koishi',
|
|
43
|
+
})
|
|
44
|
+
// webhook may be `AxiosError: Request failed with status code 429` error
|
|
45
|
+
} else {
|
|
46
|
+
webhook = webhooks.find(v => v.name === 'Koishi' && v.user.id === this.selfId)
|
|
47
|
+
}
|
|
48
|
+
return this.webhooks[channelId] = webhook
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async ensureWebhook(channelId: string) {
|
|
52
|
+
if (this.webhooks[channelId] === null) {
|
|
53
|
+
delete this.webhooks[channelId]
|
|
54
|
+
delete this.webhookLock[channelId]
|
|
55
|
+
}
|
|
56
|
+
if (this.webhooks[channelId]) {
|
|
57
|
+
delete this.webhookLock[channelId]
|
|
58
|
+
return this.webhooks[channelId]
|
|
59
|
+
}
|
|
60
|
+
return this.webhookLock[channelId] ||= this._ensureWebhook(channelId)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async getLogin() {
|
|
64
|
+
const data = await this.internal.getCurrentUser()
|
|
65
|
+
this.user = Discord.decodeUser(data)
|
|
66
|
+
return this.toJSON()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async deleteMessage(channelId: string, messageId: string) {
|
|
70
|
+
await this.internal.deleteMessage(channelId, messageId)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async editMessage(channelId: string, messageId: string, content: Fragment) {
|
|
74
|
+
const elements = h.normalize(content)
|
|
75
|
+
content = elements.toString()
|
|
76
|
+
const image = elements.find(v => v.type === 'image')
|
|
77
|
+
if (image) {
|
|
78
|
+
throw new Error("You can't include embed object(s) while editing message.")
|
|
79
|
+
}
|
|
80
|
+
await this.internal.editMessage(channelId, messageId, {
|
|
81
|
+
content,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getMessage(channelId: string, messageId: string) {
|
|
86
|
+
const data = await this.internal.getChannelMessage(channelId, messageId)
|
|
87
|
+
return await Discord.decodeMessage(this, data, {})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async getMessageList(channelId: string, before?: string) {
|
|
91
|
+
const messages = await this.internal.getChannelMessages(channelId, { before, limit: 100 })
|
|
92
|
+
const data = await Promise.all(messages.reverse().map(data => Discord.decodeMessage(this, data, {}, undefined, false)))
|
|
93
|
+
return { data, next: data[0]?.id }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getUser(userId: string) {
|
|
97
|
+
const data = await this.internal.getUser(userId)
|
|
98
|
+
return Discord.decodeUser(data)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async getGuildMemberList(guildId: string, after?: string) {
|
|
102
|
+
const users = await this.internal.listGuildMembers(guildId, { after, limit: 1000 })
|
|
103
|
+
const data = users.map(v => Discord.decodeGuildMember(v))
|
|
104
|
+
return { data, next: data[999]?.user.id }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async getChannel(channelId: string) {
|
|
108
|
+
const data = await this.internal.getChannel(channelId)
|
|
109
|
+
return Discord.decodeChannel(data)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async getGuildMember(guildId: string, userId: string) {
|
|
113
|
+
const member = await this.internal.getGuildMember(guildId, userId)
|
|
114
|
+
return Discord.decodeGuildMember(member)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async kickGuildMember(guildId: string, userId: string) {
|
|
118
|
+
return this.internal.removeGuildMember(guildId, userId)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getGuild(guildId: string) {
|
|
122
|
+
const data = await this.internal.getGuild(guildId)
|
|
123
|
+
return Discord.decodeGuild(data)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getGuildList(after?: string) {
|
|
127
|
+
const guilds = await this.internal.getCurrentUserGuilds({ after, limit: 200 })
|
|
128
|
+
const data = guilds.map(Discord.decodeGuild)
|
|
129
|
+
return { data, next: data[199]?.id }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async getChannelList(guildId: string) {
|
|
133
|
+
const channels = await this.internal.getGuildChannels(guildId)
|
|
134
|
+
return { data: channels.map(Discord.decodeChannel) }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
createReaction(channelId: string, messageId: string, emoji: string) {
|
|
138
|
+
return this.internal.createReaction(channelId, messageId, emoji)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
deleteReaction(channelId: string, messageId: string, emoji: string, userId?: string) {
|
|
142
|
+
if (!userId) {
|
|
143
|
+
return this.internal.deleteOwnReaction(channelId, messageId, emoji)
|
|
144
|
+
} else {
|
|
145
|
+
return this.internal.deleteUserReaction(channelId, messageId, emoji, userId)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
clearReaction(channelId: string, messageId: string, emoji?: string) {
|
|
150
|
+
if (!emoji) {
|
|
151
|
+
return this.internal.deleteAllReactions(channelId, messageId)
|
|
152
|
+
} else {
|
|
153
|
+
return this.internal.deleteAllReactionsForEmoji(channelId, messageId, emoji)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getReactionList(channelId: string, messageId: string, emoji: string, after?: string) {
|
|
158
|
+
const data = await this.internal.getReactions(channelId, messageId, emoji, { after, limit: 100 })
|
|
159
|
+
return { data: data.map(Discord.decodeUser), next: data[99]?.id }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
setGuildMemberRole(guildId: string, userId: string, roleId: string) {
|
|
163
|
+
return this.internal.addGuildMemberRole(guildId, userId, roleId)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
unsetGuildMemberRole(guildId: string, userId: string, roleId: string) {
|
|
167
|
+
return this.internal.removeGuildMemberRole(guildId, userId, roleId)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async getGuildRoleList(guildId: string) {
|
|
171
|
+
const data = await this.internal.getGuildRoles(guildId)
|
|
172
|
+
return { data: data.map(Discord.decodeRole) }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async createGuildRole(guildId: string, data: Partial<Universal.GuildRole>) {
|
|
176
|
+
const role = await this.internal.createGuildRole(guildId, Discord.encodeRole(data))
|
|
177
|
+
return Discord.decodeRole(role)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async updateGuildRole(guildId: string, roleId: string, data: Partial<Universal.GuildRole>) {
|
|
181
|
+
await this.internal.modifyGuildRole(guildId, roleId, Discord.encodeRole(data))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
deleteGuildRole(guildId: string, roleId: string) {
|
|
185
|
+
return this.internal.deleteGuildRole(guildId, roleId)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async createDirectChannel(userId: string) {
|
|
189
|
+
const channel = await this.internal.createDM({ recipient_id: userId })
|
|
190
|
+
return Discord.decodeChannel(channel)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async updateCommands(commands: Universal.Command[]) {
|
|
194
|
+
if (!this.config.slash) return
|
|
195
|
+
this.commands = commands
|
|
196
|
+
const updates = commands.map(Discord.encodeCommand)
|
|
197
|
+
if (updates.length) {
|
|
198
|
+
logger.debug('update %d command(s)', updates.length)
|
|
199
|
+
await this.internal.bulkOverwriteGlobalApplicationCommands(this.selfId, updates)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export namespace DiscordBot {
|
|
205
|
+
export interface Config extends Quester.Config, DiscordMessageEncoder.Config, WsClient.Config {
|
|
206
|
+
token: string
|
|
207
|
+
slash?: boolean
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const Config: Schema<Config> = Schema.intersect([
|
|
211
|
+
Schema.object({
|
|
212
|
+
token: Schema.string().description('机器人的用户令牌。').role('secret').required(),
|
|
213
|
+
}),
|
|
214
|
+
Schema.object({
|
|
215
|
+
slash: Schema.boolean().description('是否启用斜线指令。').default(true),
|
|
216
|
+
}).description('功能设置'),
|
|
217
|
+
WsClient.Config,
|
|
218
|
+
DiscordMessageEncoder.Config,
|
|
219
|
+
Quester.createConfig('https://discord.com/api/v10'),
|
|
220
|
+
])
|
|
221
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DiscordBot } from './bot'
|
|
2
|
+
import * as Discord from './utils'
|
|
3
|
+
|
|
4
|
+
export { Discord }
|
|
5
|
+
|
|
6
|
+
export * from './bot'
|
|
7
|
+
export * from './message'
|
|
8
|
+
export * from './ws'
|
|
9
|
+
|
|
10
|
+
export default DiscordBot
|
|
11
|
+
|
|
12
|
+
type ParamCase<S extends string> =
|
|
13
|
+
| S extends `${infer L}${infer R}`
|
|
14
|
+
? `${L extends '_' ? '-' : Lowercase<L>}${ParamCase<R>}`
|
|
15
|
+
: S
|
|
16
|
+
|
|
17
|
+
type DiscordEvents = {
|
|
18
|
+
[T in keyof Discord.GatewayEvents as `discord/${ParamCase<T>}`]: (input: Discord.GatewayEvents[T]) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare module '@satorijs/core' {
|
|
22
|
+
interface Session {
|
|
23
|
+
discord?: Discord.Gateway.Payload & Discord.Internal
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface Events extends DiscordEvents {}
|
|
27
|
+
}
|